如何突破網(wǎng)站對selenium的屏蔽mitmproxy

使用selenium模擬瀏覽器進(jìn)行數(shù)據(jù)抓取無疑是當(dāng)下最通用的數(shù)據(jù)采集方案厦凤,它通吃各種數(shù)據(jù)加載方式鼻吮,能夠繞過客戶JS加密,繞過爬蟲檢測较鼓,繞過簽名機(jī)制椎木。它的應(yīng)用,使得許多網(wǎng)站的反采集策略形同虛設(shè)博烂。由于selenium不會在HTTP請求數(shù)據(jù)中留下指紋香椎,因此無法被網(wǎng)站直接識別和攔截。

這是不是就意味著selenium真的就無法被網(wǎng)站屏蔽了呢禽篱?非也士鸥。selenium在運(yùn)行的時(shí)候會暴露出一些預(yù)定義的Javascript變量(特征字符串),例如"window.navigator.webdriver"谆级,在非selenium環(huán)境下其值為undefined烤礁,而在selenium環(huán)境下,其值為true(如下圖所示為selenium驅(qū)動下Chrome控制臺打印出的值)肥照。

image.png

除此之外脚仔,還有一些其它的標(biāo)志性字符串(不同的瀏覽器可能會有所不同),常見的特征串如下所示:

webdriver  
__driver_evaluate  
__webdriver_evaluate  
__selenium_evaluate  
__fxdriver_evaluate  
__driver_unwrapped  
__webdriver_unwrapped  
__selenium_unwrapped  
__fxdriver_unwrapped  
_Selenium_IDE_Recorder  
_selenium  
calledSelenium  
_WEBDRIVER_ELEM_CACHE  
ChromeDriverw  
driver-evaluate  
webdriver-evaluate  
selenium-evaluate  
webdriverCommand  
webdriver-evaluate-response  
__webdriverFunc  
__webdriver_script_fn  
__$webdriverAsyncExecutor  
__lastWatirAlert  
__lastWatirConfirm  
__lastWatirPrompt  
$chrome_asyncScriptInfo  
$cdc_asdjflasutopfhvcZLmcfl_  

了解了這個(gè)特點(diǎn)之后舆绎,就可以在瀏覽器客戶端JS中通過檢測這些特征串來判斷當(dāng)前是否使用了selenium鲤脏,并將檢測結(jié)果附加到后續(xù)請求之中,這樣服務(wù)端就能識別并攔截后續(xù)的請求吕朵。

下面講一個(gè)具體的例子猎醇。

鯤之鵬的技術(shù)人員近期就發(fā)現(xiàn)了一個(gè)能夠有效檢測并屏蔽selenium的網(wǎng)站應(yīng)用:驗(yàn)證碼表單頁,如果是正常的瀏覽器操作努溃,能夠有效的通過驗(yàn)證硫嘶,但如果是使用selenium就會被識別,即便驗(yàn)證碼輸入正確梧税,也會被提示“請求異常,拒絕操作”沦疾,無法通過驗(yàn)證(如下圖所示)称近。
image.png

可以看到它檢測了"webdriver", "__driver_evaluate", "__webdriver_evaluate"等等這些selenium的特征串。提交驗(yàn)證碼的時(shí)候抓包可以看到一個(gè)_token參數(shù)(很長)哮塞,selenium檢測結(jié)果應(yīng)該就包含在該參數(shù)里刨秆,服務(wù)端借以判斷“請求異常,拒絕操作”。

現(xiàn)在才進(jìn)入正題忆畅,如何突破網(wǎng)站的這種屏蔽呢衡未?

我們已經(jīng)知道了屏蔽的原理,只要我們能夠隱藏這些特征串就可以了家凯。但是還不能直接刪除這些屬性眠屎,因?yàn)檫@樣可能會導(dǎo)致selenium不能正常工作了。我們采用曲線救國的方法肆饶,使用中間人代理,比如fidder, proxy2.py或者mitmproxy岖常,將JS文件(本例是yoda.*.js這個(gè)文件)中的特征字符串給過濾掉(或者替換掉驯镊,比如替換成根本不存在的特征串),讓它無法正常工作竭鞍,從而達(dá)到讓客戶端腳本檢測不到selenium的效果板惑。

下面我們驗(yàn)證下這個(gè)思路。這里我們使用mitmproxy實(shí)現(xiàn)中間人代理)偎快,對JS文件(本例是yoda.*.js這個(gè)文件)內(nèi)容進(jìn)行過濾冯乘。啟動mitmproxy代理并加載response處理腳本:

mitmdump.exe -S modify_response.py

其中modify_response.py腳本如下所示:

# coding: utf-8  
# modify_response.py  
  
import re  
from mitmproxy import ctx  
    
def response(flow):  
  """修改應(yīng)答數(shù)據(jù) 
  """  
  if '/js/yoda.' in flow.request.url:  
      # 屏蔽selenium檢測 
      for webdriver_key in ['webdriver', '__driver_evaluate', '__webdriver_evaluate', '__selenium_evaluate', '__fxdriver_evaluate', '__driver_unwrapped', '__webdriver_unwrapped', '__selenium_unwrapped', '__fxdriver_unwrapped', '_Selenium_IDE_Recorder', '_selenium', 'calledSelenium', '_WEBDRIVER_ELEM_CACHE', 'ChromeDriverw', 'driver-evaluate', 'webdriver-evaluate', 'selenium-evaluate', 'webdriverCommand', 'webdriver-evaluate-response', '__webdriverFunc', '__webdriver_script_fn', '__$webdriverAsyncExecutor', '__lastWatirAlert', '__lastWatirConfirm', '__lastWatirPrompt', '$chrome_asyncScriptInfo', '$cdc_asdjflasutopfhvcZLmcfl_']:  
          ctx.log.info('Remove "{}" from {}.'.format(webdriver_key, flow.request.url))  
          flow.response.text = flow.response.text.replace('"{}"'.format(webdriver_key), '"NO-SUCH-ATTR"')  
      flow.response.text = flow.response.text.replace('t.webdriver', 'false')  
      flow.response.text = flow.response.text.replace('ChromeDriver', '') 

在selnium中使用該代理(mitmproxy默認(rèn)監(jiān)聽127.0.0.1:8080)訪問目標(biāo)網(wǎng)站,mitmproxy將過濾JS中的特征符串晒夹,如下圖所示:


image.png

經(jīng)多次測試裆馒,該方法可以有效的繞過selenium檢測,成功提交驗(yàn)證碼表單丐怯。

抄自:http://www.site-digger.com/html/articles/20180821/653.html
成功


附上mitmproxy簡單介紹(格式渣喷好,可以直接訪問原文)
抄自 https://blog.wolfogre.com/posts/usage-of-mitmproxy/


本文是一個(gè)較為完整的 mitmproxy 教程,側(cè)重于介紹如何開發(fā)攔截腳本读跷,幫助讀者能夠快速得到一個(gè)自定義的代理工具梗搅。

本文假設(shè)讀者有基本的 python 知識,且已經(jīng)安裝好了一個(gè) python 3 開發(fā)環(huán)境效览。如果你對 nodejs 的熟悉程度大于對 python无切,可移步到 anyproxy,anyproxy 的功能與 mitmproxy 基本一致丐枉,但使用 js 編寫定制腳本哆键。除此之外我就不知道有什么其他類似的工具了,如果你知道瘦锹,歡迎評論告訴我洼哎。

本文基于 mitmproxy v4烫映,當(dāng)前版本號為 [v4.0.1]
(https://blog.wolfogre.com/redirect/v3/Ax7R98RpJpVlv3sgL6mHzyQSAwM8Cv46xcU7LxImWv3FLS8tPHP6U8UtLy08c_pTxSFXLjbFyDvFbf40bv4wbv4xMRIDAzwK_jrFxVoWBjtuQQYW3Dsh_cU8Bk0KxaQE-cwFzC0vLTxz-lPF)。

顧名思義噩峦,mitmproxy 就是用于 MITM 的 proxy锭沟,MITM 即中間人攻擊(Man-in-the-middle attack)。用于中間人攻擊的代理首先會向正常的代理一樣轉(zhuǎn)發(fā)請求识补,保障服務(wù)端與客戶端的通信族淮,其次,會適時(shí)的查凭涂、記錄其截獲的數(shù)據(jù)祝辣,或篡改數(shù)據(jù),引發(fā)服務(wù)端或客戶端特定的行為切油。

不同于 fiddler 或 wireshark 等抓包工具蝙斜,mitmproxy 不僅可以截獲請求幫助開發(fā)者查看、分析澎胡,更可以通過自定義腳本進(jìn)行二次開發(fā)孕荠。舉例來說,利用 fiddler 可以過濾出瀏覽器對某個(gè)特定 url 的請求攻谁,并查看稚伍、分析其數(shù)據(jù),但實(shí)現(xiàn)不了高度定制化的需求戚宦,類似于:“截獲對瀏覽器對該 url 的請求个曙,將返回內(nèi)容置空,并將真實(shí)的返回內(nèi)容存到某個(gè)數(shù)據(jù)庫受楼,出現(xiàn)異常時(shí)發(fā)出郵件通知”垦搬。而對于 mitmproxy,這樣的需求可以通過載入自定義 python 腳本輕松實(shí)現(xiàn)艳汽。

但 mitmproxy 并不會真的對無辜的人發(fā)起中間人攻擊悼沿,由于 mitmproxy 工作在 HTTP 層,而當(dāng)前 HTTPS 的普及讓客戶端擁有了檢測并規(guī)避中間人攻擊的能力骚灸,所以要讓 mitmproxy 能夠正常工作糟趾,必須要讓客戶端(APP 或?yàn)g覽器)主動信任 mitmproxy 的 SSL 證書,或忽略證書異常甚牲,這也就意味著 APP 或?yàn)g覽器是屬于開發(fā)者本人的——顯而易見义郑,這不是在做黑產(chǎn),而是在做開發(fā)或測試丈钙。

那這樣的工具有什么實(shí)際意義呢非驮?據(jù)我所知目前比較廣泛的應(yīng)用是做仿真爬蟲,即利用手機(jī)模擬器雏赦、無頭瀏覽器來爬取 APP 或網(wǎng)站的數(shù)據(jù)劫笙,mitmproxy 作為代理可以攔截芙扎、存儲爬蟲獲取到的數(shù)據(jù),或修改數(shù)據(jù)調(diào)整爬蟲的行為填大。

事實(shí)上戒洼,以上說的僅是 mitmproxy 以正向代理模式工作的情況,通過調(diào)整配置允华,mitmproxy 還可以作為透明代理圈浇、反向代理、上游代理靴寂、SOCKS 代理等磷蜀,但這些工作模式針對 mitmproxy 來說似乎不大常用,故本文僅討論正向代理模式百炬。

安裝
“安裝 mitmproxy”這句話是有歧義的褐隆,既可以指“安裝 mitmproxy 工具”,也可以指“安裝 python 的 mitmproxy 包”剖踊,注意后者是包含前者的庶弃。
pip install mitmproxy (windows)
運(yùn)行
要啟動 mitmproxy 用 mitmproxy、mitmdump蜜宪、mitmweb 這三個(gè)命令中的任意一個(gè)即可,這三個(gè)命令功能一致祥山,且都可以加載自定義腳本圃验,唯一的區(qū)別是交互界面的不同。(我一般使用 mitmdump -s 腳本.py 命令運(yùn)行)
腳本
腳本的編寫需要遵循 mitmproxy 規(guī)定的套路缝呕,這樣的套路有兩個(gè)澳窑,使用時(shí)選其中一個(gè)套路即可。
第一個(gè)套路是供常,編寫一個(gè) py 文件供 mitmproxy 加載摊聋,文件中定義了若干函數(shù),這些函數(shù)實(shí)現(xiàn)了某些 mitmproxy 提供的事件栈暇,mitmproxy 會在某個(gè)事件發(fā)生時(shí)調(diào)用對應(yīng)的函數(shù)麻裁,形如:

import mitmproxy.http
from mitmproxy import ctx

num = 0


def request(flow: mitmproxy.http.HTTPFlow):
    global num
    num = num + 1
    ctx.log.info("We've seen %d flows" % num)

第二個(gè)套路(我沒成功)是,編寫一個(gè) py 文件供 mitmproxy 加載源祈,文件定義了變量 addons煎源,addons 是個(gè)數(shù)組,每個(gè)元素是一個(gè)類實(shí)例香缺,這些類有若干方法手销,這些方法實(shí)現(xiàn)了某些 mitmproxy 提供的事件,mitmproxy 會在某個(gè)事件發(fā)生時(shí)調(diào)用對應(yīng)的方法图张。這些類锋拖,稱為一個(gè)個(gè) addon诈悍,比如一個(gè)叫 Counter 的 addon:

import mitmproxy.http
from mitmproxy import ctx


class Counter:
    def __init__(self):
        self.num = 0

    def request(self, flow: mitmproxy.http.HTTPFlow):
        self.num = self.num + 1
        ctx.log.info("We've seen %d flows" % self.num)


addons = [
    Counter()
]

事件

上述的腳本估計(jì)不用我解釋相信大家也看明白了,就是當(dāng) request 發(fā)生時(shí)兽埃,計(jì)數(shù)器加一侥钳,并打印日志。這里對應(yīng)的是 request 事件讲仰,那攏共有哪些事件呢冕房?不多耙册,也不少,這里詳細(xì)介紹一下蔓同。

事件針對不同生命周期分為 5 類∑浚“生命周期”這里指在哪一個(gè)層面看待事件则北,舉例來說尚揣,同樣是一次 web 請求娜庇,我可以理解為“HTTP 請求 -> HTTP 響應(yīng)”的過程,也可以理解為“TCP 連接 -> TCP 通信 -> TCP 斷開”的過程泰偿。那么,如果我想拒絕來個(gè)某個(gè) IP 的客戶端請求蜈垮,應(yīng)當(dāng)注冊函數(shù)到針對 TCP 生命周期 的 tcp_start 事件晋南,又或者姜凄,我想阻斷對某個(gè)特定域名的請求時(shí)态秧,則應(yīng)當(dāng)注冊函數(shù)到針對 HTTP 聲明周期的 http_connect 事件愤诱。其他情況同理匣砖。

下面一段估計(jì)會又臭又長脆粥,如果你沒有耐心看完,那至少看掉針對 HTTP 生命周期的事件鲜棠,然后跳到示例盒音。
1,針對 HTTP 生命周期
def http_connect(self, flow: mitmproxy.http.HTTPFlow):
(Called when) 收到了來自客戶端的 HTTP CONNECT 請求。在 flow 上設(shè)置非 2xx 響應(yīng)將返回該響應(yīng)并斷開連接譬圣。CONNECT 不是常用的 HTTP 請求方法瓮恭,目的是與服務(wù)器建立代理連接,僅是 client 與 proxy 的之間的交流厘熟,所以 CONNECT 請求不會觸發(fā) request屯蹦、response 等其他常規(guī)的 HTTP 事件。

def requestheaders(self, flow: mitmproxy.http.HTTPFlow):
(Called when) 來自客戶端的 HTTP 請求的頭部被成功讀取绳姨。此時(shí) flow 中的 request 的 body 是空的登澜。

def request(self, flow: mitmproxy.http.HTTPFlow):
(Called when) 來自客戶端的 HTTP 請求被成功完整讀取。

def responseheaders(self, flow: mitmproxy.http.HTTPFlow):
(Called when) 來自服務(wù)端的 HTTP 響應(yīng)的頭部被成功讀取就缆。此時(shí) flow 中的 response 的 body 是空的帖渠。

def response(self, flow: mitmproxy.http.HTTPFlow):
(Called when) 來自服務(wù)端端的 HTTP 響應(yīng)被成功完整讀取。

def error(self, flow: mitmproxy.http.HTTPFlow):
(Called when) 發(fā)生了一個(gè) HTTP 錯誤竭宰。比如無效的服務(wù)端響應(yīng)空郊、連接斷開等。注意與“有效的 HTTP 錯誤返回”不是一回事切揭,后者是一個(gè)正確的服務(wù)端響應(yīng)狞甚,只是 HTTP code 表示錯誤而已。

  1. 針對 TCP 生命周期
    (懶得抄)
  2. 針對 Websocket 生命周期
    (懶得抄)
  3. 針對網(wǎng)絡(luò)連接生命周期
    (懶得抄)
  4. 通用生命周期
    (懶得抄)
    事實(shí)上考慮到 mitmproxy 的實(shí)際使用場景廓旬,大多數(shù)情況下我們只會用到針對 HTTP 生命周期的幾個(gè)事件哼审。再精簡一點(diǎn),甚至只需要用到 http_connect孕豹、request涩盾、response 三個(gè)事件就能完成大多數(shù)需求了
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市励背,隨后出現(xiàn)的幾起案子春霍,更是在濱河造成了極大的恐慌,老刑警劉巖叶眉,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件址儒,死亡現(xiàn)場離奇詭異,居然都是意外死亡衅疙,警方通過查閱死者的電腦和手機(jī)莲趣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來饱溢,“玉大人喧伞,你說我怎么就攤上這事。” “怎么了潘鲫?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵绿聘,是天一觀的道長。 經(jīng)常有香客問我次舌,道長熄攘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任彼念,我火速辦了婚禮挪圾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘逐沙。我一直安慰自己哲思,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布吩案。 她就那樣靜靜地躺著棚赔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪徘郭。 梳的紋絲不亂的頭發(fā)上靠益,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天,我揣著相機(jī)與錄音残揉,去河邊找鬼胧后。 笑死,一個(gè)胖子當(dāng)著我的面吹牛抱环,可吹牛的內(nèi)容都是我干的壳快。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼镇草,長吁一口氣:“原來是場噩夢啊……” “哼眶痰!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起梯啤,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤竖伯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后条辟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體黔夭,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宏胯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年羽嫡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肩袍。...
    茶點(diǎn)故事閱讀 38,100評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡杭棵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情魂爪,我是刑警寧澤先舷,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站滓侍,受9級特大地震影響蒋川,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜撩笆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一捺球、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧夕冲,春花似錦氮兵、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至弥姻,卻和暖如春南片,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背庭敦。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工铃绒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人螺捐。 一個(gè)月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓颠悬,卻偏偏與公主長得像脖阵,于是被迫代替她去往敵國和親良漱。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容