上文我转载了两篇关于ThinkPHP令牌验证的文章(​​ThinkPHP中的create方法与自动令牌验证​​)。其中提及到了  token ,这里针对 token 的作用,转载了另外两篇文章。

(​​web安全之token​​   ​​Web安全之CSRF攻击​​)


web安全之token

Token,就是令牌,最大的特点就是随机性,不可预测。一般黑客或软件无法猜测出来。 

那么,Token有什么作用?又是什么原理呢? 

Token一般用在两个地方: 

1)防止表单重复提交、 2)anti csrf攻击(跨站点请求伪造)。 


两者在原理上都是通过session token来实现的。当客户端请求页面时,服务器会生成一个随机数Token,并且将Token放置到session当中,然后将Token发给客户端(一般通过构造hidden表单)。下次客户端提交请求时,Token会随着表单一起提交到服务器端。 
然后,如果应用于"anti csrf攻击",则服务器端会对Token值进行验证,判断是否和session中的Token值相等,若相等,则可以证明请求有效,不是伪造的。 
不过,如果应用于"防止表单重复提交",服务器端第一次验证相同过后,会将session中的Token值更新下,若用户重复提交,第二次的验证判断将失败,因为用户提交的表单中的Token没变,但服务器端session中Token已经改变了。 

上面的session应用相对安全,但也叫繁琐,同时当多页面多请求时,必须采用多Token同时生成的方法,这样占用更多资源,执行效率会降低。因此,也可用cookie存储验证信息的方法来代替session Token。比如,应对"重复提交"时,当第一次提交后便把已经提交的信息写到cookie中,当第二次提交时,由于cookie已经有提交记录,因此第二次提交会失败。 
不过,cookie存储有个致命弱点,如果cookie被劫持(xss攻击很容易得到用户cookie),那么又一次gameover。黑客将直接实现csrf攻击。 

所以,安全和高效相对的。具体问题具体对待吧。 

要避免"加token但不进行校验"的情况,在session中增加了token,但服务端没有对token进行验证,根本起不到防范的作用。 

对数据库有改动的增删改操作,需要加token验证,对于查询操作,一定不要加token,防止攻击者通过查询操作获取token进行csrf攻击。但并不是这样攻击者就无法获得token,只是增大攻击成本而已。 





Web安全之CSRF攻击


CSRF是什么? 

CSRF(Cross Site Request Forgery),中文是跨站点请求伪造。CSRF攻击者在用户已经登录目标网站之后,诱使用户访问一个攻击页面,利用目标网站对用户的信任,以用户身份在攻击页面对目标网站发起伪造用户操作的请求,达到攻击目的。 


举个例子 简单版: 

假如博客园有个加关注的GET接口,blogUserGuid参数很明显是关注人Id,如下: 

那我只需要在我的一篇博文内容里面写一个img标签: 

那么只要有人打开我这篇博文,那就会自动关注我。 


升级版: 

假如博客园还是有个加关注的接口,不过已经限制了只获取POST请求的数据。这个时候就做一个第三方的页面,但里面包含form提交代码,然后通过QQ、邮箱等社交工具传播,诱惑用户去打开,那打开过博客园的用户就中招了。 

在说例子之前要纠正一个iframe问题,有人会直接在第三方页面这样写。如下: 

<!DOCTYPE HTML> 
<html lang="en-US">
<head>
<title>CSRF SHOW</title>
</head>
<body>
<!--不嵌iframe会跳转-->
<iframe style="display:none;">
<form name="form1" action="http://www.cnblogs.com/mvc/Follow/FollowBlogger.aspx" method="post">
<input type="hidden" name="blogUserGuid" value="4e8c33d0-77fe-df11-ac81-842b2b196315"/>
<input type="submit" value>
</form>
<script>
  document.forms.form1.submit();
</script> </iframe> </body> </html>

同源策略的原因,iframe内容根本加载不出来,所以里面form提交当然不会执行。 

PS:我尝试了chrome、IE11、Firefox,情况都是这样。 

所以可以用嵌多一层页面方式解决,如下: 

第一个展示页面(test): 

<!DOCTYPE HTML> 
<html lang="en-US">
<head>
<title>CSRF SHOW</title>
</head>
<body>
<iframe style="display:none;" src="test2.html"></iframe>
</body>
</html>


第二个隐藏页面(test2): 



<!DOCTYPE HTML> 
<html lang="en-US">
<head>
<title>CSRF GET</title>
<body>
<form name="form1" action="http://www.cnblogs.com/mvc/Follow/FollowBlogger.aspx" method="post">
<input type="hidden" name="blogUserGuid" value="4e8c33d0-77fe-df11-ac81-842b2b196315"/>
<input type="submit" value>
</form>
<script>
  document.forms.form1.submit();
</script> </body> </html>

form可以跨域post数据。 


进阶版: 

假如博客园还是有个加关注的接口,已经限制POST,但博文内容是直接贴进HTML(未过滤),那就遭受XSS攻击。那么就可以直接把上面代码嵌入博文,那么只要有人打开我这篇博文,还是会自动关注我,这组合攻击方式称为XSRF。 

CSRF攻击的本质原因 

Web的隐式身份验证机制!Web的身份验证机制虽然可以保证一个请求是来自于某个用户的浏览器,但却无法保证该请求是用户批准发送的。CSRF攻击的一般是由服务端解决。 

CSRF工具的防御手段 

1. 尽量使用POST,限制GET 

GET接口太容易被拿来做CSRF攻击,看第一个示例就知道,只要构造一个img标签,而img标签又是不能过滤的数据。接口最好限制为POST使用,GET则无效,降低攻击风险。 

当然POST并不是万无一失,攻击者只要构造一个form就可以,但需要在第三方页面做,这样就增加暴露的可能性。 

2. 浏览器Cookie策略 

IE6、7、8、Safari会默认拦截第三方本地Cookie(Third-party Cookie)的发送。但是Firefox2、3、Opera、Chrome、Android等不会拦截,所以通过浏览器Cookie策略来防御CSRF攻击不靠谱,只能说是降低了风险。 

PS:Cookie分为两种,Session Cookie(在浏览器关闭后,就会失效,保存到内存里),Third-party Cookie(即只有到了Exprie时间后才会失效的Cookie,这种Cookie会保存到本地)。 

PS:另外如果网站返回HTTP头包含P3P Header,那么将允许浏览器发送第三方Cookie。 


3. 加验证码 

验证码,强制用户必须与应用进行交互,才能完成最终请求。在通常情况下,验证码能很好遏制CSRF攻击。但是出于用户体验考虑,网站不能给所有的操作都加上验证码。因此验证码只能作为一种辅助手段,不能作为主要解决方案。 

4. Referer Check 

Referer Check在Web最常见的应用就是"防止图片盗链"。同理,Referer Check也可以被用于检查请求是否来自合法的"源"(Referer值是否是指定页面,或者网站的域),如果都不是,那么就极可能是CSRF攻击。 

但是因为服务器并不是什么时候都能取到Referer,所以也无法作为CSRF防御的主要手段。但是用Referer Check来监控CSRF攻击的发生,倒是一种可行的方法。 

5. Anti CSRF Token 

现在业界对CSRF的防御,一致的做法是使用一个Token(Anti CSRF Token)。 


例子: 

1. 用户访问某个表单页面。 

2. 服务端生成一个Token,放在用户的Session中,或者浏览器的Cookie中。 

3. 在页面表单附带上Token参数。 

4. 用户提交请求后, 服务端验证表单中的Token是否与用户Session(或Cookies)中的Token一致,一致为合法请求,不是则非法请求。 

Token的保密性,尽量把敏感操作由GET改为POST,以form或AJAX形式提交,避免Token泄露。 


注意: 

CSRF的Token仅仅用于对抗CSRF攻击。当网站同时存在XSS漏洞时候,那这个方案也是空谈。所以XSS带来的问题,应该使用XSS的防御方案予以解决。 


总结 

CSRF攻击是攻击者利用用户的身份操作用户帐户的一种攻击方式,通常使用Anti CSRF Token来防御CSRF攻击,同时要注意Token的保密性和随机性。 


参考文献: 

1. 《​​浅谈CSRF攻击方式​​》 

2. 《白帽子讲Web安全》 

本文为原创文章,转载请保留原出处,方便溯源,如有错误地方,谢谢指正。