背景
去年在公司寫(xiě)過(guò)一個(gè)爬蟲(chóng)工具,用于抓取自動(dòng)化報(bào)告通過(guò)率判莉、自動(dòng)發(fā)送報(bào)告豆挽。由于當(dāng)時(shí)是第一次接觸爬蟲(chóng),難免會(huì)遇到各種問(wèn)題券盅,解決方案全都是按照網(wǎng)上的一些爬蟲(chóng)文章示例帮哈,照貓畫(huà)虎寫(xiě)的。雖然能正常使用渗饮,但其實(shí)很多地方都沒(méi)弄明白但汞。最近學(xué)習(xí)了一些前端和后臺(tái)的原理,了解了cookie與session的機(jī)制互站,總算弄明白了爬蟲(chóng)登錄過(guò)程中的一個(gè)疑問(wèn)。
用戶登錄請(qǐng)求中的authenticity_token
編寫(xiě)爬蟲(chóng)第一步僵缺,在登錄公司的自動(dòng)化平臺(tái)時(shí)就遇到了一個(gè)難題胡桃,登錄請(qǐng)求中必須包含一個(gè)authenticity_token字段。令人頭大的是磕潮,完全不知道這個(gè)字段從何而來(lái)翠胰,而且該字段還每次都不一樣,參考的爬蟲(chóng)登錄示例也沒(méi)教白愿之景!真是急壞苯寶寶了??
后來(lái)翻了好多CSDN的爬蟲(chóng)貼,了解到知乎的登錄請(qǐng)求中也包含這樣一個(gè)字段膏潮,而作者的處理方式就是先訪問(wèn)一次登錄頁(yè)锻狗,然后從登錄頁(yè)中查找一個(gè)隱藏的authenticity_token字段。
借助F12發(fā)現(xiàn)焕参,公司的自動(dòng)化平臺(tái)登錄頁(yè)中也包含了這樣一個(gè)隱藏字段轻纪,試之,果然成功了......
#登錄源碼:
def login(login_url = 'http://****.com/users/sign_in', username, password):
#請(qǐng)求頭
my_headers = {
'User-Agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36',
'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Encoding' : 'gzip',
'Accept-Language' : 'zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4'
}
#獲取token
sss = requests.Session()
r = sss.get(login_url, headers = my_headers)
reg = r'<input name="authenticity_token" type="hidden" value="(.*)" />'
pattern = re.compile(reg)
result = pattern.findall(r.content)
token = result[0]
#postdata
my_data = {
'commit' : '登錄',
'utf8' : '%E2%9C%93',
'authenticity_token' : token,
'user[email]': username,
'user[password]':password
}
#登錄后
r = sss.post(login_url, headers = my_headers, data = my_data)
return sss
"多年后的一個(gè)平靜的下午叠纷,當(dāng)我無(wú)意間瀏覽了一片CSRF攻擊的帖子刻帚,突然眼前一亮......老衲終于明白了這個(gè)authenticity_token的含義了!I崇众!終于徹底理解了當(dāng)年困擾我兩小時(shí)的難題了5嘟!顷歌!"
其實(shí)看峻,該token的作用就是防御CSRF攻擊,關(guān)于什么是CSRF衙吩,還得先了解下Session id互妓。
關(guān)于Session id的機(jī)制
HTTP請(qǐng)求的一大特點(diǎn)就是無(wú)狀態(tài),這也就導(dǎo)致服務(wù)端無(wú)法區(qū)分請(qǐng)求來(lái)自哪個(gè)客戶端坤塞。為了記錄每個(gè)用戶的狀態(tài)冯勉,跟蹤用戶的整個(gè)會(huì)話,web程序普遍采用了cookie與session技術(shù)摹芙。(由于cookie與session的內(nèi)容過(guò)多灼狰,在此不表,詳細(xì)原理可以參考一片文章:Cookie與Session機(jī)制)
關(guān)于cookie與session浮禾,最需要了解的幾點(diǎn)是:
- session機(jī)制運(yùn)行依賴(lài)于session id交胚,用于服務(wù)端跟蹤每個(gè)會(huì)話,而session id存在于本地的cookie當(dāng)中盈电;
- session id會(huì)隨瀏覽器進(jìn)程關(guān)閉的關(guān)閉而清除蝴簇,也就表示一次完整的會(huì)話結(jié)束了。當(dāng)下次再次訪問(wèn)該網(wǎng)站還需要登錄匆帚,重新建立一個(gè)會(huì)話熬词;
- 現(xiàn)在絕大多數(shù)瀏覽器都支持子窗體,子窗體能共享父窗體的session id吸重,而另起的瀏覽器進(jìn)程無(wú)法訪問(wèn)該session互拾。這也是為什么當(dāng)我們?cè)谀尘W(wǎng)站登錄后,在新的頁(yè)簽下打開(kāi)該網(wǎng)站依然是登錄狀態(tài)嚎幸,而另起一個(gè)瀏覽器進(jìn)程訪問(wèn)卻是非登錄狀態(tài)颜矿。
根據(jù)session機(jī)制以上特點(diǎn),就引申出了一個(gè)問(wèn)題:CSRF攻擊嫉晶。
什么是跨站請(qǐng)求偽造(CSRF)攻擊骑疆?
用戶每次點(diǎn)擊一個(gè)鏈接、提交一個(gè)表單车遂,其本質(zhì)就是對(duì)服務(wù)端發(fā)起一次請(qǐng)求封断。而CSRF攻擊的原理就是:攻擊者誘導(dǎo)用戶點(diǎn)擊一個(gè)鏈接,用戶在不知情的情況下提交了一次表單請(qǐng)求舶担。而表單的內(nèi)容則是攻擊者事先準(zhǔn)備好的坡疼。
簡(jiǎn)單舉個(gè)栗子??:
- 用戶小明登錄了論壇A,同時(shí)也打開(kāi)了一個(gè)危險(xiǎn)網(wǎng)站B(同一個(gè)瀏覽器中)衣陶;
- 網(wǎng)站B上有一個(gè)鏈接柄瑰,該鏈接的實(shí)質(zhì)內(nèi)容是針對(duì)論壇A的一個(gè)發(fā)帖請(qǐng)求(比如廣告貼)闸氮。
- 小明處于好奇點(diǎn)擊了該鏈接,造成的結(jié)果就是:小明在完全不知情的情況下在論壇A成功發(fā)表了一篇帖子教沾。
備注: 以上攻擊成功實(shí)施的關(guān)鍵在于蒲跨,小明已經(jīng)登錄論壇A,并且點(diǎn)擊跳轉(zhuǎn)后的瀏覽器子窗體是可以訪問(wèn)父窗體的session id的授翻。
假如小明復(fù)制該鏈接或悲,然后手動(dòng)打開(kāi)一個(gè)新的瀏覽器粘貼訪問(wèn)該鏈接,則會(huì)提示用戶處于非登錄狀態(tài)堪唐,該發(fā)帖請(qǐng)求會(huì)被拒絕巡语。原因是新打開(kāi)的瀏覽器無(wú)法獲取前一個(gè)瀏覽器中的session id,服務(wù)端會(huì)將該請(qǐng)求當(dāng)成一個(gè)新的會(huì)話淮菠,需要重新登錄后才能成功執(zhí)行發(fā)帖請(qǐng)求男公。
CRSF攻擊防御
既然大家都了解CRSF攻擊,自然有相應(yīng)的防御措施合陵,其中比較常用的就是采用token驗(yàn)證枢赔。
工作機(jī)制就是:用戶在發(fā)送表單時(shí)還需要攜帶一個(gè)token值。該token一般是填寫(xiě)表單頁(yè)中的一個(gè)隱藏字段拥知,每次訪問(wèn)都不同踏拜。通過(guò)該token的驗(yàn)證,服務(wù)端就能知道用戶的表單請(qǐng)求是否從表單填寫(xiě)頁(yè)面跳轉(zhuǎn)而來(lái)了举庶。
簡(jiǎn)單舉例:
- 當(dāng)小明主動(dòng)發(fā)帖時(shí)执隧,必定要先點(diǎn)擊發(fā)帖編輯頁(yè)面A,當(dāng)填寫(xiě)完帖子內(nèi)容后再點(diǎn)擊【發(fā)帖】按鈕户侥。此時(shí)會(huì)將小明填寫(xiě)的表單內(nèi)容連帶頁(yè)面A中隱藏的一個(gè)token發(fā)送給服務(wù)端。服務(wù)端驗(yàn)證token通過(guò)后才表示發(fā)帖成功峦嗤。
- 當(dāng)危險(xiǎn)網(wǎng)站誘導(dǎo)小明點(diǎn)擊危險(xiǎn)鏈接時(shí)蕊唐,由于該鏈接實(shí)質(zhì)就是一個(gè)發(fā)帖的post請(qǐng)求,跳過(guò)了訪問(wèn)發(fā)帖編輯頁(yè)面A的過(guò)程烁设,自然也就無(wú)法獲取有效token替梨,最終服務(wù)端會(huì)認(rèn)為該發(fā)帖請(qǐng)求不合法。
簡(jiǎn)單來(lái)說(shuō)装黑,服務(wù)端每次通過(guò)請(qǐng)求數(shù)據(jù)中的token來(lái)驗(yàn)證表單請(qǐng)求是否由用戶主動(dòng)發(fā)送的副瀑,從而有效防御了CRSF攻擊。
至此恋谭,也就明白了為什么登錄頁(yè)面時(shí)需要攜帶一個(gè)authenticity_token參數(shù)了糠睡,同時(shí)也理解了為什么需要訪問(wèn)登錄頁(yè)面獲取該token。??