網站防刷方案

網站重複請求解決方案

Mr. Neo Chen (陳景峯), netkiller, BG7NYT


中國廣東省深圳市龍華新區民治街道溪山美地
518131
+86 13113668890


版權聲明

轉載請與作者聯繫,轉載時請務必標明文章原始出處和作者信息及本聲明。

文檔出處:
http://netkiller.github.io
http://netkiller.sourceforge.net

微信掃瞄二維碼進入 Netkiller 微信訂閲號

QQ群:128659835 請註明“讀者”

摘要

這是講述如何防止重複請求你的網站, 包括如,爬蟲,數據採集,刷排名,批量註冊,批量發帖,利用漏洞獲取網站數據等等。


目錄

1. 訪問網站所涉及環節

簡單說就是重複相同的請求

首先看看訪問流程所設計的每個環節

		
User -> Browse -> CDN/Proxy Cache -> Web Server -> App Server / fastcgi pool -> Cache -> Database
		
		

大部分網站都是這樣的結構:用戶,瀏覽器,CDN或反向代理,Web伺服器,應用伺服器,緩存,資料庫

這個訪問過程中所涉及的設備

		
PC -> ADSL/Cable/Ethernet -> Route -> ... -> Route -> Firewall -> Load Balance -> Switch -> Server
		
		

我們看看從那些環節可以截獲用戶的刷新行為

可控制環節
  1. CDN / 反向代理,提供一些基本防護功能,過于簡單。

  2. 3/4層設備,防火牆/路由器/交換機,主要還是靠防火牆設備,例如Cisco ASA 系列防火牆,都提供IPS/IDS服務(需要單獨採購,設備預設沒有)主要是針對IP地址的請求頻率做出策略控制,

  3. 4/7 層負載均衡設備, 一半負載均衡設備都附帶此功能。但不是他主要的功能,沒有能力購買防火牆設備的中小公司可以使用該功能,7層功能非常強大,但都是通用功能,不一定滿足你的個性化需求。

  4. 瀏覽器,這是主要是改變瀏覽器端設置,利用Cookie變化,Javascript等技術,阻止重複請求

  5. WEB 伺服器,在web上通過擴展模組與相應的配置也能達到一定的效果

  6. 應用伺服器, 主要是通過編寫程序在阻止惡意訪問。

依次從上至下,越能提前在上一層阻止行為越好,否則就在下一層截獲。

2. 瀏覽器款控制方案

通過 Javascript 防止重複點擊提交按鈕,通常的做法是將按鈕禁用 通過 disabled 屬性實現。下面是Jquery例子

$("form").submit(function(){
  $(":submit",this).attr("disabled","disabled");
});
		

在上面的例子基礎上可以改良,增加計時器,限制一定時間內不可重複提交。

通過 Cookie技術控制重複訪問動作

		
訪問第一個頁面 login.example.com/form.ext 的時候設置一個 cookie 變數
訪問第二個頁面 login.example.com/auth.ext 的時候判斷上一個頁面設置的 cookie 是否有效,如果無效拒絶訪問。
		
		

可以進一步增加難度,例如用戶註冊分為很多步驟,每一個步驟都會設置一個標記,如果用戶行為不是按照順序訪問,直接在最後一個頁面提交,明顯可以判斷是非法行為。

這裡的方案是針對人工操作,更多的時採用程序實現刷新,採集,爬蟲等等。

3. CDN 與 反向代理

CDN 都提供一些基本的防護功能,主要是針對 IP 地址, URL 做一些限制

如果自己做反向代理,控制權更大,可以充分使用操作系統帶的包過濾防火牆與代理軟件所提供的7層功能

由於很多web server 具備代理伺服器功能,配置也相差無幾,所有在後面web server 會詳細介紹。

4. 網絡設備控制方法

每一個網絡設備使用方法都不同,這裡無法舉例,但原理都是相同的。

3/4 層網絡設備可以按照IP地址與連接埠號訪問情況做具體限制,如單位時間內允許的訪問次數,這種對於大量的攻擊比較有效

7層網絡設備功能非常強大,就可以根據HTTP頭做規則策略,如限制URL的單位時間訪問的IP數量,判斷 Cookie 等信息,

5. 伺服器上做控制

這部分分為,操作系統與web伺服器兩個部分

5.1. 操作系統部分

操作系統部分,主要是通過linux內核提供的包過濾功能,通常所說的iptables

iptables -A INPUT -p icmp -m limit --limit 3/s -j LOG --log-level INFO --log-prefix "ICMP packet IN: "

iptables -N syn-flood
iptables -A INPUT -p tcp --syn -j syn-flood
iptables -I syn-flood -p tcp -m limit --limit 3/s --limit-burst 6 -j RETURN
iptables -A syn-flood -j REJECT
			

限制源IP的訪問數量

-A INPUT -p tcp -m state --state NEW -m tcp --dport 80 --tcp-flags FIN,SYN,RST,ACK SYN -m connlimit --connlimit-above 50 --connlimit-mask 32 -j REJECT --reject-with icmp-port-unreachable
-A INPUT -p tcp -m state --state NEW -m tcp --dport 443 --tcp-flags FIN,SYN,RST,ACK SYN -m connlimit --connlimit-above 50 --connlimit-mask 32 -j REJECT --reject-with icmp-port-unreachable
			

關鍵字,字元串過略

			
iptables -A INPUT -p tcp --dport 80 -m string --algo bm --string "XXDD0S" -j DROP
			
			

以上所講都是被動方法,需要系統管理一條一條添加規則。

提示

基于IP與連接埠的方法有明顯的不足,經常會誤將某些正常的IP地址封鎖。

下面通過腳本實現主動防禦,通過提取 access.log 檔案定位更精準,同時實現了黑/白名單可以將安全IP放置在白名單中。

			
#!/bin/bash
########################################
# Homepage: http://netkiller.github.io
# Author: neo <netkiller@msn.com>
########################################
PIPE=/tmp/pipe
pidfile=/tmp/firewall.pid

ACCCESS_LOG=/tmp/access.log
TIMEPOINT='24/May/2012'
BLACKLIST=/var/tmp/black.lst
WHITELIST=/var/tmp/white.lst
########################################

if [ -z "$( egrep "CentOS|Redhat" /etc/issue)" ]; then
	echo 'Only for Redhat or CentOS'
	exit
fi

if [ ! -f ${BLACKLIST} ]; then
    touch ${BLACKLIST}
fi

if [ ! -f ${WHITELIST} ]; then
    touch ${WHITELIST}
fi

for deny in $(grep ${TIMEPOINT} ${ACCCESS_LOG} | awk '{print $1}' | awk -F'.' '{print $1"."$2"."$3"."$4}' | sort | uniq -c | sort -r -n | head -n 30| awk '{print $2}')
do

    if [ $(grep -c $deny ${WHITELIST}) -ne 0 ]; then
        echo 'Allow IP:' $deny
	iptables -D INPUT -p tcp --dport 443 -s $deny -j DROP
	iptables -D INPUT -p tcp --dport 80 -s $deny -j DROP
	continue
    fi

    if [ $(grep -c $deny ${BLACKLIST}) -eq 0 ] ; then

	echo 'Deny IP:' $deny
        echo $deny >> ${BLACKLIST}
        iptables -I INPUT -p tcp --dport 443 -s $deny -j DROP
        iptables -I INPUT -p tcp --dport 80 -s $deny -j DROP
    fi
done
			
			

相比前面腳本,這個腳本更高級,實現關鍵字過濾,管道實時處理,這樣不回因為日誌尺寸變大,影響到腳本的處理性能。

			
#!/bin/bash
########################################
# Homepage: http://netkiller.github.io
# Author: neo <netkiller@msn.com>
########################################
ACCESSLOG=/www/logs/www.example.com/access.$(date +'%Y-%m-%d').log
TIMEPOINT='24/May/2012'
KEYWORD=send.php
BLACKLIST=/var/tmp/black.lst
WHITELIST=/var/tmp/white.lst
PIPE=/var/tmp/pipe
pidfile=/var/tmp/firewall.pid
logfile=/var/tmp/firewall.log
########################################
if [ -z "$( egrep "CentOS|Redhat" /etc/issue)" ]; then
	echo 'Only for Redhat or CentOS'
	exit
fi

if [ -z $1 ]; then
    echo "$0 clear|fw|collect|process|close"
fi

if [ "$1" == "clear" ]; then
    rm -rf $BLACKLIST
    rm -rf $PIPE
    echo "Clear OK!!!"
fi

if [ "$1" == "close" ]; then
	killall tail
    kill `cat $pidfile`
    echo > $pidfile
fi

if [ ! -e $PIPE ]; then
    mkfifo $PIPE
fi

if [ "$1" == 'fw' ]; then
    iptables -A OUTPUT -p tcp --dport 2049 -j REJECT
    iptables -A OUTPUT -p tcp -m multiport --dports 22,21 -j REJECT

	for ipaddr in ${WHITELIST}
	do
		if [ $(grep -c $ipaddr ${WHITELIST}) -ne 0 ]; then
			iptables -A INPUT -p tcp --dport 443 -s $ipaddr -j ACCEPT
			iptables -A INPUT -p tcp --dport 80 -s $ipaddr -j ACCEPT
			echo 'Allow IP:' $ipaddr >> $logfile
		fi
		if [ $(grep -c $ipaddr ${BLACKLIST}) -eq 0 ] ; then
			iptables -D INPUT -p tcp --dport 443 -s $ipaddr -j DROP
			iptables -D INPUT -p tcp --dport 80 -s $ipaddr -j DROP
			echo 'Deny IP:' $ipaddr

		fi
	done

fi

if [ "$1" == "collect" ]; then
    killall tail
    for (( ; ; ))
    do
        tail -f $ACCESSLOG | grep $KEYWORD | cut -d ' ' -f1 > $PIPE
    done &
    echo $! > $pidfile
fi

if [ "$1" == "process" ]; then

	if [ ! -f $BLACKLIST ]; then
		touch $BLACKLIST
	fi

	if [ ! -f ${WHITELIST} ]; then
		touch ${WHITELIST}
	fi

	for (( ; ; ))
	do
		while read ipaddr
		do
			if [ $(grep -c $ipaddr ${WHITELIST}) -ne 0 ]; then
				echo 'Allow IP:' $ipaddr >> $logfile
				continue
			fi

			grep $ipaddr ${BLACKLIST}
			if [ $? -eq 1 ] ; then
				echo $ipaddr >> ${BLACKLIST}
				iptables -I INPUT -p tcp --dport 80 -s $ipaddr -j DROP
				echo "Deny IP: $ipaddr" >> $logfile
			fi
		done < $PIPE
	done &
	echo $! >> $pidfile
fi
			
			

5.2. WEB 伺服器部分

下面所講技術,適用於反向代理,負載均衡,web伺服器

Web 伺服器也可以實現前面所說的防火牆等設備3/4層的功能,同時具備七層功能,很多負載均衡設備7層採用web伺服器實現,例如 F5 7層的高級功能是由 Apache httpd 來完成(apache 是經過二次開發的), 所以7層的部門我們主要在這裡深入討論

如果你有防火牆設備應該首先考慮在防火牆端做控制,如果沒有防火牆那麼就考慮在負載均衡設備中做控制,這些設備你都沒有,最後考慮在反向代理中處理,最後考慮web伺服器。

限制IP地址在這裡可以做到更細膩的控制,例如實現某個目錄的,某URL的IP訪問策略。請自行查找手冊或參考《Netkiller Web 手札》

HTTP 協議頭

我們要做以下幾種限制
  1. 限制 http_referer, 常說的防盜鏈。

  2. 限制 http_user_agent, 主要是防爬蟲

  3. 限制 request_method, 不是所有頁面都允許 POST

  4. 限制 http_cookie, 沒有攜帶正確的 cookie 不允許訪問

上面7層訪問控制還是比較粗糙的,主要是給應用程序減壓,更細膩的控制需要通過程序手段,實現更智能判斷。 不過同上上面的層層限制,已經足矣改善你的狀況,如果還是無效繼續往下看。

			
valid_referers none blocked *.example.com example.com;
if ($invalid_referer) {
	#rewrite ^(.*)$  http://www.example.com/cn/$1;
	return 403;
}		
			
			
if ($http_user_agent = "") { 
	return 403; 
}		
			

6. 通過程序控制訪問行為

設計應用防火牆,將所有資源納入管理範圍

IP地址,上面已經反覆強調怎樣封鎖IP地址,但都過于粗糙,很多時候是一刀切。在程序中實現禁止IP訪問,更靈活

我們要做以下幾種限制
  1. 單位時間內訪問次數

  2. 訪問時間間隔設置

  3. 封鎖時間設置

  4. 黑白名單


		

驗證碼,最常用的,最有效的方法,分為圖片扭曲法,問提/答案 法,手機驗證碼,語音驗證碼等等方法,形式多重多樣

http_referer, 雖然上面已經做了 http_referer 限制,但是web 伺服器只能做粗糙限制,允許 *.example.com 域進行訪問,但我需要更精確的控制。例如:

		
www.example.com (此時 http_referer 為空,或者其他,這不重要) -> login.example.com (http_referer: www.example.com)-> login.example.com/auth.ext (http_referer: login.example.com) -> login.example.com/secussed.ext (http_referer: login.example.com/auth.ext)
		  
		

看明白了嗎 http_referer 每次都是上一個頁面,我們程序中判斷,如果上一個頁面不是我們所指定的,或者不再允許列表內,就拒絶訪問

request_method

		
www.example.com (GET) -> login.example.com (GET)-> login.example.com/auth.ext (POST) -> login.example.com/secussed.ext (GET)		
		
		

同理,在不允許的頁面POST操作,將立即拒絶

http_cookie

www.example.com (cookie 1) -> login.example.com (cookie 2)-> login.example.com/auth.ext (cookie 3) -> login.example.com/secussed.ext (cookie 4)		
		

沒有按照指定流程訪問,cookie 值不會變化,屬於異常行為

cookie + redis 間隔時間

提示

上面所有的操作都將計入日誌,通過腳本可以將異常訪問行文達到一定次數後,放入iptables DROP鏈中。

7. 總結

上面提方法單一使用過于簡單,需要組合使用,同時經常調整組合方式才能更有效阻止各種良性與惡性網站訪問行為。