咱们知道,通常防护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!