版權聲明
轉載請與作者聯繫,轉載時請務必標明文章原始出處和作者信息及本聲明。
|
|
|
微信掃瞄二維碼進入 Netkiller 微信訂閲號 QQ群:128659835 請註明“讀者” |
這是講述如何防止重複請求你的網站, 包括如,爬蟲,數據採集,刷排名,批量註冊,批量發帖,利用漏洞獲取網站數據等等。
簡單說就是重複相同的請求
首先看看訪問流程所設計的每個環節
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
我們看看從那些環節可以截獲用戶的刷新行為
CDN / 反向代理,提供一些基本防護功能,過于簡單。
3/4層設備,防火牆/路由器/交換機,主要還是靠防火牆設備,例如Cisco ASA 系列防火牆,都提供IPS/IDS服務(需要單獨採購,設備預設沒有)主要是針對IP地址的請求頻率做出策略控制,
4/7 層負載均衡設備, 一半負載均衡設備都附帶此功能。但不是他主要的功能,沒有能力購買防火牆設備的中小公司可以使用該功能,7層功能非常強大,但都是通用功能,不一定滿足你的個性化需求。
瀏覽器,這是主要是改變瀏覽器端設置,利用Cookie變化,Javascript等技術,阻止重複請求
WEB 伺服器,在web上通過擴展模組與相應的配置也能達到一定的效果
應用伺服器, 主要是通過編寫程序在阻止惡意訪問。
依次從上至下,越能提前在上一層阻止行為越好,否則就在下一層截獲。
通過 Javascript 防止重複點擊提交按鈕,通常的做法是將按鈕禁用 通過 disabled 屬性實現。下面是Jquery例子
$("form").submit(function(){ $(":submit",this).attr("disabled","disabled"); });
在上面的例子基礎上可以改良,增加計時器,限制一定時間內不可重複提交。
通過 Cookie技術控制重複訪問動作
訪問第一個頁面 login.example.com/form.ext 的時候設置一個 cookie 變數 訪問第二個頁面 login.example.com/auth.ext 的時候判斷上一個頁面設置的 cookie 是否有效,如果無效拒絶訪問。
可以進一步增加難度,例如用戶註冊分為很多步驟,每一個步驟都會設置一個標記,如果用戶行為不是按照順序訪問,直接在最後一個頁面提交,明顯可以判斷是非法行為。
這裡的方案是針對人工操作,更多的時採用程序實現刷新,採集,爬蟲等等。
CDN 都提供一些基本的防護功能,主要是針對 IP 地址, URL 做一些限制
如果自己做反向代理,控制權更大,可以充分使用操作系統帶的包過濾防火牆與代理軟件所提供的7層功能
由於很多web server 具備代理伺服器功能,配置也相差無幾,所有在後面web server 會詳細介紹。
每一個網絡設備使用方法都不同,這裡無法舉例,但原理都是相同的。
3/4 層網絡設備可以按照IP地址與連接埠號訪問情況做具體限制,如單位時間內允許的訪問次數,這種對於大量的攻擊比較有效
7層網絡設備功能非常強大,就可以根據HTTP頭做規則策略,如限制URL的單位時間訪問的IP數量,判斷 Cookie 等信息,
這部分分為,操作系統與web伺服器兩個部分
操作系統部分,主要是通過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
下面所講技術,適用於反向代理,負載均衡,web伺服器
Web 伺服器也可以實現前面所說的防火牆等設備3/4層的功能,同時具備七層功能,很多負載均衡設備7層採用web伺服器實現,例如 F5 7層的高級功能是由 Apache httpd 來完成(apache 是經過二次開發的), 所以7層的部門我們主要在這裡深入討論
如果你有防火牆設備應該首先考慮在防火牆端做控制,如果沒有防火牆那麼就考慮在負載均衡設備中做控制,這些設備你都沒有,最後考慮在反向代理中處理,最後考慮web伺服器。
限制IP地址在這裡可以做到更細膩的控制,例如實現某個目錄的,某URL的IP訪問策略。請自行查找手冊或參考《Netkiller Web 手札》
HTTP 協議頭
限制 http_referer, 常說的防盜鏈。
限制 http_user_agent, 主要是防爬蟲
限制 request_method, 不是所有頁面都允許 POST
限制 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; }
設計應用防火牆,將所有資源納入管理範圍
IP地址,上面已經反覆強調怎樣封鎖IP地址,但都過于粗糙,很多時候是一刀切。在程序中實現禁止IP訪問,更靈活
單位時間內訪問次數
訪問時間間隔設置
封鎖時間設置
黑白名單
驗證碼,最常用的,最有效的方法,分為圖片扭曲法,問提/答案 法,手機驗證碼,語音驗證碼等等方法,形式多重多樣
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鏈中。
上面提方法單一使用過于簡單,需要組合使用,同時經常調整組合方式才能更有效阻止各種良性與惡性網站訪問行為。