這是我在知乎的一個回答。原提問是如何在單頁應用下進行 XSRF 防禦。html
XSRF(CSRF) 攻擊的原理是什麼?就是攻擊者能猜想出全部的須要提交的內容以及類型,因此全部的解決方案共同出發點就是加一個攻擊者也不知道隨機值發送給後端驗證就能夠防範。前端
有不少解決方案,cookie-session,很不友好的全部表單都得填寫驗證碼,還有一種不多人知道 JSON Web Token。ios
驗證碼(圖形或者手機)這種就不說了吧,這個在互聯網場景中由於用戶體驗緣由幾乎沒有應用的。ajax
首先要知道直接讓後端驗證 cookie 是否存在正確是不可取的,由於全部請求都會自動附帶請求所在域的 cookie,固然只驗證 http referrer 也是不靠譜的。axios
正確的方式是當用戶進行登陸請求的時候,這時候後端應該把包含 xsrf 字段的 cookie 保存在 session 中而且返還給前端,前端須要獲取到 cookie 中的值而且能放入 ajax 請求體或請求頭中,後端把這個值與 session 中的相應值進行判斷就能夠了,根據跨域不可訪問不一樣域的 cookie ,攻擊者也很難猜想出 xsrf 的值,那麼這樣就防範了 xsrf 攻擊。後端
因此這裏對 xsrf cookie 不能設置 httpOnly(固然就會有 XSS 問題,後面會提),同時提一句因此的 Token 必須得讓後端設置 expire 過時時間。跨域
這個 axios 就提供了這個功能,只要設置約定好 xsrf cookie字段名就能夠了,axios 獲取到值後默認是放入 request header 中,這也是業界最流行的方式。緩存
若是不是單頁應用都是後端在表單中加入一個隱藏的表單域。安全
<input type="hidden" name="_token" value="lAfHB..">
固然還有JWT,這個主要應用場景是 app,由於 app 一般沒有 Cookie,固然也有應用到 Web 中的,要講這個就有點多了,和上述也差很少。服務器
簡單說,JWT 就是服務端和客戶端約定好一個Token格式,最後用密鑰進行簽名 base64 編碼後放入請求頭便可,客戶端存放這個簽名的內容一般會放在 localstorage 中,也有放在 cookie 中的。JWT應用了哈希簽名的密碼學技術,相比 cookie-session 的方式就是服務端能夠不用(在內存或者緩存)存放 session,能節省存儲資源,不過同時服務器須要經過計算來驗證也浪費了計算資源。詳細的說明能夠參考:講真,別再使用JWT了!
現有的產品爲了更安全還須要考慮 XSS 攻擊,這個就是有些惡意腳本或者插件不存在跨域問題,因此能獲取到 cookie 和 localstorage 的值。
很安全的方式就是把 XSRF Token 加入到 JWT 中,而且把 JWT 存放在設置 httpOnly 的 cookie 中,而後單獨把 XSRF Token 設置在 httpOnly=false 的 cookie 中,前端請求時,須要獲取 XSRF Token 並放入請求頭(RequestHeader)。服務器端能夠直接驗證JWT中XSRF的值和XSRF的值便可。由於用了哈希密鑰簽名的技術,這樣就能夠防止篡改內容。
這樣的安全防禦就能抵禦全部的 XSRF 攻擊了。