理解CSRF(跨站請(qǐng)求偽造)
對(duì)于Express團(tuán)隊(duì)的csrf模塊和csurf模塊的加密函數(shù)的用法我們經(jīng)常有一些在意碴里。
這些在意是莫須有的,因?yàn)樗麄儾涣私釩SRF token是如何工作的变勇。
下面快速過(guò)一遍牡拇!
讀過(guò)后還有疑問(wèn)?希望告訴我們錯(cuò)誤?請(qǐng)開(kāi)一個(gè)issue事秀!
一個(gè)CSRF攻擊是如何工作的?
在他們的釣魚(yú)站點(diǎn)野舶,攻擊者可以通過(guò)創(chuàng)建一個(gè)AJAX按鈕或者表單來(lái)針對(duì)你的網(wǎng)站創(chuàng)建一個(gè)請(qǐng)求:
<form action="https://my.site.com/me/something-destructive" method="POST">
<button type="submit">Click here for free money!</button>
</form>
這是很危險(xiǎn)的易迹,因?yàn)楣粽呖梢允褂闷渌鹔ttp方法例如 delete
來(lái)獲取結(jié)果。
這在用戶的session中有很多關(guān)于你的網(wǎng)站的詳細(xì)信息時(shí)是相當(dāng)危險(xiǎn)的平道。
如果一個(gè)不懂技術(shù)的用戶遇到了睹欲,他們就有可能會(huì)輸入信用卡號(hào)或者個(gè)人安全信息。
如果減輕CSRF攻擊一屋?
只使用JSON api
使用JavaScript發(fā)起AJAX請(qǐng)求是限制跨域的窘疮。
不能通過(guò)一個(gè)簡(jiǎn)單的<form>
來(lái)發(fā)送JSON
,
所以冀墨,通過(guò)只接收J(rèn)SON闸衫,你可以降低發(fā)生上面那種情況的可能性。
禁用CORS
第一種減輕CSRF攻擊的方法是禁用cross-origin requests(跨域請(qǐng)求)诽嘉。
如果你希望允許跨域請(qǐng)求楚堤,那么請(qǐng)只允許 OPTIONS, HEAD, GET
方法疫蔓,因?yàn)樗麄儧](méi)有副作用。
不幸的是身冬,這不會(huì)阻止上面的請(qǐng)求由于它沒(méi)有使用JavaScript(因此CORS不適用)衅胀。
檢驗(yàn)referrer頭部
不幸的是,檢驗(yàn)referrer頭部很麻煩酥筝,
但是你可以阻止那些referrer頭部不是來(lái)自你的頁(yè)面的請(qǐng)求滚躯。
這實(shí)在不值得麻煩。
舉個(gè)例子嘿歌,你不能加載session如果這個(gè)請(qǐng)求的referrer頭部不是你的服務(wù)器掸掏。
GET總是冪等的
確保你的GET
請(qǐng)求不會(huì)修改你數(shù)據(jù)庫(kù)中的相關(guān)數(shù)據(jù)。
這是一個(gè)初學(xué)者常犯的錯(cuò)誤宙帝,使得你的應(yīng)用不僅是易于遭受CSRF攻擊丧凤。
避免使用POST
因?yàn)?code><form>只能用GET
或是POST
,
而不能使用別的方法,例如PUT
, PATCH
, DELETE
步脓,
攻擊者很難有方法攻擊你的網(wǎng)站愿待。
不要復(fù)寫(xiě)方法
許多應(yīng)用程序使用復(fù)寫(xiě)方法來(lái)在一個(gè)常規(guī)表單中使用PUT
, PATCH
, 和DELETE
請(qǐng)求。
這會(huì)使得原先不易受攻擊的方法變得易受攻擊靴患。
不要兼容舊瀏覽器
舊的瀏覽器不支持CORS或是其他安全政策仍侥。
通過(guò)不兼容舊瀏覽器
(那些不懂技術(shù)的人用的越多,我們?cè)饺菀妆还?,
你可以最小化受到攻擊的可能性鸳君。
CSRF Tokens
最終的解決辦法是使用CSRF tokens农渊。
CSRF tokens是如何工作的呢?
- 服務(wù)器發(fā)送給客戶端一個(gè)token或颊。
- 客戶端提交的表單中帶著這個(gè)token砸紊。
- 如果這個(gè)token不合法,那么服務(wù)器拒絕這個(gè)請(qǐng)求囱挑。
攻擊者需要通過(guò)某種手段獲取你站點(diǎn)的CSRF token醉顽,
他們只能使用JavaScript來(lái)做。
所以看铆,如果你的站點(diǎn)不支持CORS,
那么他們就沒(méi)有辦法來(lái)獲取CSRF token盛末,
降低了威脅弹惦。
確保CSRF token不能通過(guò)AJAX訪問(wèn)到!
不要?jiǎng)?chuàng)建一個(gè)/CSRF
路由來(lái)獲取一個(gè)token,
尤其不要在這個(gè)路由上支持CORS!
token需要是不容易被猜到的悄但,
讓它很難被攻擊者嘗試幾次得到棠隐。
它不需要是密碼安全的。
攻擊來(lái)自從一個(gè)未知的用戶的一次或者兩次的點(diǎn)擊檐嚣,
而不是來(lái)自一臺(tái)服務(wù)器的暴力攻擊助泽。
BREACH攻擊
這也就是salt(加鹽)出現(xiàn)的原因啰扛。
Breach攻擊相當(dāng)簡(jiǎn)單:如果服務(wù)器通過(guò)HTTPS+gzip
多次發(fā)送相同或者相似的響應(yīng),攻擊者就可以猜測(cè)響應(yīng)的內(nèi)容(使得HTTPS完全無(wú)用)嗡贺。
解決辦法隐解?讓每一個(gè)響應(yīng)都有那么一點(diǎn)不同。
于是诫睬,CSRF tokens依據(jù)每一個(gè)不同的請(qǐng)求還有不同的時(shí)間來(lái)生成煞茫。
但是服務(wù)器需要知道客戶端請(qǐng)求中帶的token是否是合法的。
因此:
- 一般認(rèn)為安全加密的CSRF tokens是防護(hù)CSRF的關(guān)鍵
- CSRF tokens現(xiàn)在通常是一個(gè)秘鑰或者salt的hash
了解更多:
注意摄凡,CSRF沒(méi)有_解決_BREACH攻擊续徽,
但是這個(gè)模塊通過(guò)隨機(jī)化請(qǐng)求來(lái)為你減輕BREACH攻擊。
salt不需要加密
因?yàn)榭蛻舳酥纒alt!!!
服務(wù)器會(huì)發(fā)送 <salt>;<token>
亲澡,然后客戶端會(huì)通過(guò)請(qǐng)求返回相同的值給服務(wù)器钦扭。服務(wù)器然后會(huì)檢驗(yàn) <secret>+<salt>=<token>
。
salt必須跟token一起被發(fā)送給服務(wù)器床绪,否則服務(wù)器不能驗(yàn)證這個(gè)token客情。
這是最簡(jiǎn)單的加密方式。
還有很多方法会涎,不過(guò)他們更加復(fù)雜裹匙,犯不著那么麻煩。
創(chuàng)建tokens必須要快
因?yàn)槊慨?dāng)進(jìn)來(lái)一個(gè)請(qǐng)求他們就會(huì)被創(chuàng)建!
像Math.random().toString(36).slice(2)
這么做也是性能足夠好的!
你不需要OpenSSL來(lái)為每一個(gè)請(qǐng)求創(chuàng)建一個(gè)密碼安全的token末秃。
秘鑰不需要是加密的概页,但需要是安全的
如果你正在使用一個(gè)數(shù)據(jù)庫(kù)后端來(lái)存儲(chǔ)session,客戶端是不會(huì)知道秘鑰的练慕,因?yàn)樗淮鎯?chǔ)在數(shù)據(jù)庫(kù)中惰匙。
如果你正在使用cookie來(lái)存儲(chǔ)session,那么秘鑰就會(huì)被存儲(chǔ)在cookie中發(fā)送給客戶端铃将。
因此项鬼, 確保cookie sessions 使用 httpOnly
那樣客戶端就不能通過(guò)客戶端JavaScript來(lái)讀取到秘鑰!
當(dāng)你不正確的使用CSRF token
把它們加到JSON AJAX調(diào)用中
正如上面提到的,如果你不支持CORS并且你的API是傳輸?shù)膰?yán)格的JSON劲阎,
絕沒(méi)可能在你的AJAX 調(diào)用中加入CSRF token绘盟。
通過(guò)AJAX暴露你的CSRF token
不要?jiǎng)?chuàng)建一個(gè)GET /csrf
路由
并且尤其不要在這個(gè)路由上支持CORS。
不要發(fā)送CSRF token在API響應(yīng)的body中悯仙。
結(jié)論
因?yàn)閣eb正在向JSON API轉(zhuǎn)移龄毡,并且瀏覽器變得更安全,有更多的安全策略锡垄,
CSRF正在變得不那么值得關(guān)注沦零。
阻止舊的瀏覽器訪問(wèn)你的站點(diǎn),并盡可能的將你的API變成JSON API货岭,
然后你將不再需要CSRF token路操。
但是為了安全起見(jiàn)疾渴,你還是應(yīng)該盡量允許他們尤其是當(dāng)難以實(shí)現(xiàn)的時(shí)候。