咱們知道,通常防護CSRF有三種方法,判斷referer、驗證碼、token。 php
對於判斷referer來講,雖然客戶端帶用戶狀態的跨域提交,js和as已經沒法僞造referer了;可是對於客戶端軟件和flash的提交,通常是不帶referer的,聽說一些山寨瀏覽器也不帶。那麼就須要爲此開綠燈,但這樣使得外站的flash請求僞造沒法被防護。 html
而驗證碼弊端明顯:會對用戶形成影響。 ajax
token的問題也有一些:時效性沒法保證;大型服務時,須要一臺token生成及校驗服務器;須要更改全部表單添加字段。 canvas
而最近我在作之類的防護時,想出了另一種方法,跟xeye、woyigui等人在羣裏討論了一番,認爲應該是可行的,因此拿出來分享一下,並讓其餘的牛人看看是否有什麼弊端 跨域
其實原理很是簡單,與token也差很少:當表單提交時,用js在本域添加一個臨時的cookies字段,並將過時時間設爲1秒鐘以後,而後再提交;服務端校驗有這個字段即放行,沒有則認爲是CSRF。 瀏覽器
token防csrf的原理是:沒法經過ajax等方式得到外域頁面中的token值,xmlhttprequest須要遵照瀏覽器同源策略;而臨時cookies的原理也是:cookies只能在父域和子域之間設置,也遵照同源策略。 安全
咱們能夠簡單看一個demo: 服務器
http://127.0.0.1/test.html :
<script> function doit(){ var expires = new Date((new Date()).getTime()+1000); document.cookie = "xeye=xeye; expires=" + expires.toGMTString(); } </script> <form action="http://127.0.0.1/test.php" name="f" id="f" onsubmit="doit();" target="if1"> <input type="button" value="normal submit" onclick="f.submit();"> <input type="button" value="with token" onclick="doit();f.submit();"> <input type="submit" value="hook submit"> </form> <iframe src="about:blank" name="if1" id="if1"></iframe>http://127.0.0.1/test.php:
<?php echo "<div>Cookies</div>"; var_dump($_COOKIE); ?>
test.html爲瀏覽器端的表單,裏面有三個按鈕: cookie
第一個是正常的表單提交;第二個是添加臨時cookies後提交表單;第三個是以hook submit事件來添加臨時cookies並提交。 網絡
結果就像開頭的圖片演示那樣,正常的表單提交不會出現臨時cookies字段,第二個和第三個按鈕提交則會出現。你們能夠反覆點擊按鈕來查看結果,但須要注意時間間隔需超過1秒。(固然能夠將test.html拿到外域看看,不過要注意form的target不能指向iframe了,能夠以新窗口打開。因爲同源策略,cookies確定是帶不過去的)
不過這種方式只適用於單域名站點,或者安全需求不須要「當子域發生XSS隔離父域」。由於子域是能夠操做父域的cookies的,因此它的缺陷也比較明顯:這種方法沒法防護因爲其餘子域產生的xss所進行的表單僞造提交。而一個區分分域的自校驗token是能夠防止從其餘子域到本域的提交的。但若是對於單域而言,這種方法應該是足夠的,而且安全性可能會略大於token。
和羣裏的幾位大牛討論了一下,也認爲這種方式沒有什麼大問題,不過確實有一些小的疑問,譬如:
網絡不流暢,有延遲會不會致使cookies失效。這個顯然是不會的,由於服務端cookies是在提交請求的header中得到的。延時在服務端,不在客戶端,而1秒鐘足能夠完成set Cookies+post header整個post表單的過程。
cookies的生成依賴於js,至關於這個token是明文的?這個的確,無論採起多少種加密,只要在客戶端,就會被破解,不過無論怎樣,csrf沒法在有用戶狀態的狀況下去添加這個臨時cookies字段。雖然服務端curl等能夠,可是沒法將當前用戶的狀態也帶過去。
外站是否能夠僞造這個臨時cookies呢?目前來看至少經過as和js沒法向其餘域添加和更改cookies的,經過服務端雖然能夠僞造cookies,但得到不到目標域的用戶狀態。
若是目標域有XSS就完蛋了?恩,不過通常來講判斷referer、token和簡單的驗證碼(利用canvas識別?)也差很少完蛋了。
若是因爲某種網絡問題,得到不到cookies了呢?那麼用戶狀態也不能得到了,用戶只能再提交一次了。
ok,就這些!
說實話,這種新方法到底是否真正有效我也沒譜,說不定有某種BT的方式能夠繞過?因此share一下,你們不妨看看是否真的有效。若是真有效,那麼大概是一種最簡單的,對代碼改動最小,對服務器壓力也最小的防護CSRF的方法了。
最後感謝下woyigui,提出了不少建議呵呵!
Monyer!