【乾貨】萬字長文教你對抗邪惡的爬蟲

或許是競爭對手在窺探商業機密。

或許是某個學生爲了論文而做的數據採集。

又或許只是碼農的一個惡作劇。

無論如何,一個行爲不受控制的爬蟲都會對我們的數據安全有着或多或少的威脅,它迫使着我們行動起來對抗這種威脅。

1

初級篇

知己知彼,百戰不殆

爬蟲和反爬蟲的本質是技術對抗,作爲反爬蟲方,要意識到爬蟲是狡猾的,首先需要做到的就是要知道如何區分一個請求是正常請求還是爬蟲,然後才能想出對應的措施來限制爬蟲。

(一)

基於請求頭的限制

一個初級爬蟲和正常的請求最大的區別就是請求頭的中User-Agent,所謂User-Agent就是用來標識客戶端應用程序的,不同的客戶端(甚至同一客戶端不同版本)之間都會有所區別。

首先看一下Chrome瀏覽器的User-Agent:

Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36

然後看一下IE瀏覽器的User-Agent:

Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko

最後看一下一個常用的Python爬蟲庫Requests的User-Agent:

python-requests/2.20.1

區別很明顯了,針對爬蟲的反制措施也很容易:如果User-Agent檢查沒通過,直接返回403Forbidden。

下面是一段通過User-Agent攔截請求的Nginx配置示例:

server {
        listen 80;
        server_name 192.168.50.67 www.example.com;
        
        charset utf-8;
        access_log /var/log/nginx/access.log;
        error_log /var/log/nginx/error.log;
        # 通過正則匹配,如果發現User-Agent帶有python或者postman直接返回403
        if ($http_user_agent ~* "python|postman") {
                return 403;
        }


        location / {
                proxy_pass http://127.0.0.1:5000;
        }
}


另外除了User-Agent之外,Referer和Cookies也是常用的區分爬蟲和正常請求的請求頭。

通過請求頭來攔截爬蟲的方式是所有方式中最簡單的。由於近兩年關於Python爬蟲的文章和視頻充斥着整個互聯網,可以毫不客氣的說:通過攔截User-Agent帶有Python標識的請求可以攔截百分之八十的爬蟲。

簡單高效,幾乎不會誤傷到真實用戶。不過,同時也要意識到有經驗的爬蟲作者會通過僞裝User-Agent的方式來繞過攔截。

實用指數:五星

誤傷指數:半星

繞過難度:一星半

(二)

基於IP訪問頻率的限制

初級爬蟲的另一個特點就是野蠻,就像惡龍看到金子一樣,絲毫不會掩飾自己的貪婪。因爲爬蟲的高效,相較於正常請求,如果遇到一個毫無自控力的爬蟲對於服務器的壓力是可想而知的。所以服務端限制單個IP的訪問頻率就顯得非常有必要。

Nginx本身支持針對客戶端訪問限制,下面是Nginx的配置示例:

 http {
    # 因爲http持久化連接的存在,單個連接往往可以發送多次請求 
    # limit_conn_zone 是限制單個ip的併發連接數。對應下面的limit_conn
    limit_conn_zone $binary_remote_addr zone=addr:10m;
    # limit_req_zone 是限制單個ip的併發請求數。對應下面的limit_req
    limit_req_zone $binary_remote_addr zone=one:10m rate=100r/s;
    server {
        listen       80;
        server_name  192.168.50.67 127.0.0.1;
        location / {
            proxy_pass  http://127.0.0.1:5000;
            limit_conn addr 100;
            limit_req zone=one  burst=150;
        }
    }
}

那麼,是不是服務端做了訪問限制就可以高枕無憂了呢?答案是否定的。當爬蟲作者發現頻率限制之後,往往會在很短的時間內就能夠分析出服務器配置的訪問頻率的大概範圍,如果能夠滿足自身需要,可以通過限制自己的訪問頻率來避免觸發服務器的警戒值;即使發現服務器配置的訪問頻率太嚴格,不能滿足自己的需要,也可以通過IP代理池來不斷的變換IP地址。不過好消息是,相比基於請求頭的限制,基於IP訪問頻率的限制方式很顯著的提高了爬蟲的成本,“痛則不通”,它可以幫助我們篩選掉那些對於成本敏感的爬蟲,特別是每年三月份爲了畢業論文行動起來的學生。

另外,基於IP訪問頻率的限制有一個明顯的缺點,那就是容易誤傷真實用戶。要知道現實中很可能一棟樓都在用一個IP地址。

實用指數:三星半

誤傷指數:三星半

繞過難度:三星

(二)

基於驗證碼的限制

前面兩種方式主要是從行爲特徵的角度來試圖識別某個請求是正常請求還是爬蟲。但是如果爬蟲僞裝的足夠好,會發現無論是基於請求頭還是基於訪問頻率,都已經不能夠用來判斷一個請求是不是爬蟲了。

”守則不足,攻則有餘“,這時候往往需要轉變被動防守的思維模式,主動出擊。既然基於行爲特徵已經走進了死衚衕,爲什麼不能讓用戶自己表明不是爬蟲呢?驗證碼就是在這種背景下應運而生的,最常見的驗證碼形式是圖形驗證碼和短信驗證碼。

圖形驗證碼

要求用戶主動輸入驗證碼,驗證通過之後纔可以繼續下一步操作,這種方式可以很有效的幫助後臺識別真實用戶和爬蟲。不過”道高一尺,魔高一丈“,爬蟲陣營也發展出了對抗驗證碼的技術。以圖形驗證碼爲例,普通的圖形驗證碼可以很容易的被現有的OCR技術識別。OCR技術的發展進而又導致了驗證碼中出現了噪點、干擾線、字母粘連等提高識別難度的手段。攻防對抗一旦形成,雙方往往會不計代價的投入戰鬥。以谷歌的圖形驗證碼爲例,真人識別的成功率已經不足百分之五十,不禁讓人思考這種犧牲用戶體驗的防禦方式是不是真的值得。

雖然圖形驗證碼走進了死衚衕,但是近年來出現了很多種驗證碼的變種,比如滑塊驗證、拼圖驗證以及文字點選認證等形式,儘量的把對用戶體驗的影響降到最低。

某雲廠商提供的驗證方案

實用指數:三星

誤傷指數:四星

繞過難度:四星

2

高級篇

既分高下,也決生死

在初級篇裏面,我們介紹了三種對抗方式。可以毫不客氣的說,一套組合拳打出去,百分之九十五的爬蟲都會倒地不起。

但是也要意識到剩下的百分之五纔是真正的對手,能夠突破驗證碼限制的爬蟲,往往是對服務器數據有着強烈興趣,同時有着相對較高的技術水平,對於成本也有着相對更高的預算。此時的技術對抗的主要特徵是,數據的生產方作爲防守方先出招,爬蟲方作爲進攻方見招拆招。由於此階段少不了加密技術的身影,對於防守方來說往往需要前後端開發人員協同作戰。另外,不同於驗證碼的簡單粗暴,看似緊張的高手過招,對於正常用戶而言卻是無感知的。

口說無憑,也是爲了能更好的說明問題,老張請教了一個擅長爬蟲的朋友——公輸,分享他的兩個案例跟大家詳細說說。

(一)

某團外賣API攜帶的token

2018左右,當時還有市場上的幾家外賣平臺活動力度還比較大,作爲肥宅的公輸就想着利用爬蟲找到自己家附近最優惠的商家。

雞賊的公輸選擇了外賣平臺的手機端H5平臺。我們前面提到的三種限制方式,公輸都遇到了,並且很輕易的就繞過了,當時老張還誇他就是那百分之五的高手,直到他遇到了某團外賣的token。

某團外賣展示的商家信息都是通過GET請求從服務器拉取的,每次請求的時候URL裏面都會有一個accessToken參數,服務器會校驗這個token參數,如果校驗失敗服務器會返回一個錯誤代碼。簡單的嘗試之後,公輸發現了事情並沒有那麼簡單,token的生成規則關乎外賣爬蟲的成敗。

再見到他已經是一個月之後了。

再見公輸的第一面,激增的白髮並不能掩蓋他眼神裏的故事。知道公輸大功已成的老張還沒來得及開口,公輸卻首先拋出了一個問題。

公輸:你聽說過JS混淆嗎?

老張:聽過。好好的一段代碼進去,混淆的連媽都不認識,但是調用的時候還跟沒事人一樣,用過的都說好。

一段混淆前後的JS代碼對比

公輸點頭不語,突然的沉默反而激發了老張的好奇心。

老張:所以,隱藏在混淆之後的token到底是怎麼是生成的?

聽到老張問到了關鍵點,滿頭白髮的公輸微微一驚。

公輸:罷了,跟你說了吧。首先,把所有的GET參數按照key排序組合成一個字符串,然後用zlib壓縮,最後用base64轉碼,就是最後的token了。

老張:就這麼簡單?

公輸:就這麼簡單!

老張:花了一個月?

公輸:花了一個月!

(二)

某水果iTunes的登錄流程

相信上一個例子已經很說明說明問題了:但凡正常用戶可以的做到的,爬蟲肯定也可以做到,只不過是成本或高或低。

可能有些同學不信服,覺得公輸能夠破解外賣平臺的token是因爲外賣平臺提供了可以在瀏覽器使用的服務方式,而瀏覽器是沒有祕密的,就算混淆後的JS也是可以通過開發者工具查看的,如果是客戶端提供服務,經過編譯的加密代碼肯定沒法破解。

老張:有人覺得客戶端是鐵板一塊,爬蟲遇到客戶端就會死翹翹。

公輸:呵呵,他肯定沒聽過IDA,對於逆向工程的力量一無所知。

IDA價格在幾千美刀左右,你看知名黑客炫富都這麼有內涵

老張:Talk is cheap, 你手頭有沒有案例。

公輸:還真有,你知道iTunes嗎?iTunes登錄複雜吧,不一樣搞它?

老張:知道,早期雲計算能力有限,幾乎每個水果手機用戶都需要在電腦上安裝iTunes用來同步數據。我聽說水果家的技術安全水平可是高的很。

公輸:首先,iTunes會根據主機的硬件信息生成一個MD5值,然後請求兩次服務器交換加密信息,最後才發送真正的登錄請求。

老張:等等,說的跟真的一樣,我怎麼知道你有沒有騙我?

公輸:Show you the code,那個DLL文件就是一切的關鍵。

3

終級篇

他強由他強,清風拂山崗;他橫由他橫,明月照大江

透過高級篇裏面的兩個案例,甚至可以看到矛盾論的身影:矛盾的同一性和鬥爭性共同推動了事物的發展。具體到本篇文章就是:爬蟲和反爬蟲的技術對抗共同作用,推高了雙方的技術成本。

可能到這裏有些同學會有悲觀情緒,覺得強如谷歌、微軟這樣的互聯網巨頭,也沒法完全封殺爬蟲。聽老張一句勸,其實大可不必這麼悲觀,其實在爬蟲與反爬蟲的技術對抗中,主動權一直掌握在反爬蟲一方的手中。前面驗證碼那一章提到了化被動爲主動,現在老張再幫大家解放思想,介紹一下應對爬蟲的終極手段——投毒。

前面我們已經介紹了多種的手段來識別一個請求是真實請求還是爬蟲,在意識到是爬蟲的時候,我們又是怎麼做的呢?一般來說是返回403Forbidden,禁止爬蟲的訪問。高手過招,這種情況可能會適得其反,激發了爬蟲的戰鬥慾望,不計成本的來繞過我們的反爬蟲措施。但是如果我們不是返回403Forbidden,而是返回假數據呢?害怕太明顯會被發現的話,再過分一點,如果一千次響應裏面我們參雜了四百二十五條假數據呢?因爲假數據的存在,爬蟲獲取的數據就是不可信數據,基於不可信數據甚至能推導出完全錯誤的結論,這樣就從一個刁鑽的角度保護了我方數據安全。這種明明已經識別爬蟲,卻故意返回假數據的方式就是投毒。

記得老張曾經跟公輸提起到“投毒”,老張永遠忘不了當時公輸聽完這兩個字的時候虎軀一震,彷彿PTSD患者,臉上的表情像極了在酒館遭受調侃的孔乙己。

一個反爬蟲碼農的快樂就是這麼樸實無華,且枯燥!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章