首先介紹一下CSRF的驗(yàn)證過程:
client發(fā)送get請(qǐng)求 -> 服務(wù)生成XSRF-TOKEN懂缕,存入用戶session中。返回響應(yīng)王凑,響應(yīng)包含XSRF-TOKEN cookie -> 瀏覽器接收響應(yīng)后自動(dòng)保存cookie -> js獲取此cookie的值搪柑,構(gòu)造X-XSRF-TOKEN頭部 -> 向server發(fā)送post請(qǐng)求 -> server通過client傳遞過來的包含server_id的cookie反序列化此用戶的session數(shù)據(jù) -> 從session中拿出用戶的CSRF-TOKEN與請(qǐng)求頭中的值進(jìn)行對(duì)比 -> 驗(yàn)證通過或失敗
然后想象這么一種場(chǎng)景。有一個(gè)正常域名A被XSS注入索烹,其中的惡意代碼如下:
<img src="http://bank.example.com/withdraw?account=bob&amount=1000000&for=mallory">
這時(shí)工碾,當(dāng)你打開此頁面后,瀏覽器會(huì)默認(rèn)發(fā)起http請(qǐng)求百姓,請(qǐng)求地址就是src
中的地址渊额。因?yàn)槭菫g覽器發(fā)出的請(qǐng)求,所以這個(gè)請(qǐng)求默認(rèn)會(huì)帶上此域名的cookies。如果恰好你在bank.example.com
這個(gè)域名是登陸狀態(tài)端圈,那么就會(huì)中招了焦读。
這里重點(diǎn)強(qiáng)調(diào)一下cookie的機(jī)制:瀏覽器向一個(gè)域名發(fā)起http請(qǐng)求時(shí)會(huì)帶上瀏覽器保存的關(guān)于那個(gè)域名的cookies,而不管你從哪個(gè)網(wǎng)站發(fā)請(qǐng)求舱权。上面的例子,請(qǐng)求就是從域名A發(fā)出的仑嗅。
清楚了上面的攻擊手段宴倍,對(duì)于CSRF驗(yàn)證令牌為什么放在頭部這個(gè)問題就很好理解了。為了添加新的頭部仓技,js就必須獲取XSRF-TOKEN
的值鸵贬,然后構(gòu)造X-XSRF-TOKEN
頭部。而對(duì)于上面那種攻擊手段脖捻,是沒有能力獲取cookie的阔逼。
那么問題又來了,既然域名A都已經(jīng)被入侵了地沮,那攻擊者完全可以植入js代碼嗜浮,然后獲取cookie,構(gòu)造頭部摩疑,這一系列操作不也是輕而易舉的嗎危融?
答案是,不行雷袋。因?yàn)閖s要獲取cookie存在三個(gè)因素的限制:
- Path吉殃。也就是URI的路徑,路徑不同楷怒,不允許訪問
- Doamin蛋勺。域名不同不允許訪問
- http-only。設(shè)置為true之后鸠删,js不能訪問
很顯然抱完,因?yàn)楫?dāng)前發(fā)起請(qǐng)求所在的域名和攻擊者的目標(biāo)域名不一樣,所以即使頁面被注入的js代碼冶共,也無法獲取目標(biāo)域名的cookie乾蛤。其次,對(duì)于存放session_id
的cookie捅僵,一般都設(shè)置為http-only
家卖,禁止js讀取,這也是防止xss攻擊的主要手段庙楚。
綜上所述上荡,對(duì)于存放CSRF令牌的cookie應(yīng)該取消http-only
的設(shè)置,同時(shí)session_id
cookie默認(rèn)要啟用http-only
屬性,此時(shí)后端采用通過頭部驗(yàn)證令牌的方式才能全面的防范CSRF攻擊酪捡。
備注:
- axios在發(fā)送POST請(qǐng)求時(shí)叁征,如果當(dāng)前域名存在
XSRF-TOKEN
cookie,它自動(dòng)會(huì)構(gòu)造好X-XSRF-TOKEN
頭部逛薇。 - 關(guān)于
XSRF-TOKEN
捺疼,session_id
這些cookie的名字,不同的框架可能會(huì)有所不同永罚,但是功能都是一樣的啤呼。
完!
參考資料:
MDN - Cookies
laravel - CSRF Protection
laravel的CSRF防護(hù)機(jī)制和延伸