CSRF的全名是Cross Site Request Forgery,它是一种常见的Web攻击,但很多开发者对它很陌生。CSRF也是Web安全中最容易被忽略的一种攻击方式,甚至很多安全工程师都不太理解它的利用条件和危害,因此不予重视。但CSRF在某些时候却能够产生强大的破坏性。

CSRF简介

一个例子:
攻击者首先在自己的域构造一个页面:
http://www.a.com/csrf.html
其内容为:
<img src="http://blog.sohu.com/manage/entry.do?m=delete&id=156713012" />
使用了一个<img>标签,其地址指向了删除博客文章的链接。攻击者诱使目标用户点击自己构造的页面,就可以删掉博客文章。

CSRF进阶

浏览器的Cookie策略

在刚刚的例子里,攻击者伪造的请求之所以能够被搜狐服务器验证通过,是因为用户的浏览器成功发送了Cookie的缘故。

浏览器所持有的Cookie分为两种:一种是“Session Cookie”,又称"临时Cookie";另一种是“Third-party Cookie",也称”本地Cookie“。

两者的区别在于,Third-party Cookie是服务器在Set-Cookie时指定了Expire时间,只有到了Expire时间后Cookie才会失效,所以这种Cookie会保存在本地;而Session Cookie则没有指定Expire时间,所以浏览器关闭后,Session Cookie就失效了。

如果浏览器从一个域的页面中,要加载另一个域的资源,由于安全原因,某些浏览器会阻止Third-party Cookie的发送。

但若CSRF攻击的目标并不需要使用Cookie,则也不必顾虑浏览器的Cookie策略了。

P3P头的副作用

P3P Header是W3C制定的一项关于隐私的标准,全称是The Platform for Privacy Preferences。

如果网站返回给浏览器的HTTP头中包含有P3P头,则在某种程度上来说,将允许浏览器发送第三方Cookie。

在网站的业务中,P3P头主要用于类似广告等需要跨域访问的页面。但是很遗憾的是,P3P头设置后,对于Cookie的影响将扩大到整个域中的所有页面,因为Cookie是以域和path为单位的,这并不符合“最小权限”原则。

P3P头的介入改变了a.com的隐私策略,从而使得<iframe>、<script>等标签在IE中不再拦截第三方Cookie的发送。P3P只需要由网站设置一次即可,之后每次请求都会遵循此策略,而不需要再重复设置。

正因为P3P头目前在网站的应用中被广泛应用,因此在CSRF的防御中不能依赖于浏览器对第三方Cookie的拦截策略,不能心存侥幸。

很多时候,如果测试CSRF时发现<iframe>等标签在IE中居然能发送Cookie,而又找不到原因,那么很可能就是因为P3P头在作怪。

GET?POST?

CSRF除了可以用GET请求,还可以用POST请求。

最简单的方法,就是在一个页面中构造好一个form表单,然后使用JavaScript自动提交这个表单。攻击者甚至可以将这个页面隐藏在一个不可见的iframe窗口中,那么整个自动提交表单的过程,对于用户来说也是不可见的。

Flash CSRF

Flash可以使用URLRequest、getURL、loadVars等方式发起请求。

在IE6、IE7中,Flash发送的网络请求均可以带上本地Cookie;但是从IE8起,Flash发起的网络请求已经不再发送本地Cookie了。

CSRF Worm

一个例子:

百度用户中心的发送短消息功能中,只需要修改一个参数即可对指定的用户发送短消息。而百度的另一个接口则能查询出某个用户的所有好友。

将两者结合起来,可以组成一个CSRF Worm——让一个百度用户查看恶意页面后,将给他的所有好友发送一条短消息,然后这条短消息中又包含一张图片,其地址再次指向CSRF页面,使得这些好友再次将消息发给他们的好友,这个Worm因此得以传播。

CSRF的防御

验证码

CSRF攻击的过程,往往是在用户不知情的情况下构造了网络请求。而验证码,则强制用户必须与应用进行交互,才能完成最终请求。因此在通常情况下,验证码能够很好地遏制CSRF攻击。但验证码只能作为辅助手段。

Referer Check

Referer Check可以被用于检查请求是否来自合法的“源”。

但是服务器并非什么时候都能取到Referer。

Anti CSRF Token

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

CSRF的本质

CSRF能攻击成功的本质原因是重要操作的所有参数都是可以被攻击者猜测到的。

解决方案:把参数加密,或者使用一些随机数,从而让攻击者无法猜测到参数值。

但是这个方法也存在一些问题。首先,加密或混淆后的URL将变得非常难懂,对用户非常不友好。其次,如果加密的参数每次都改变,则某些URL将无法再被用户收藏。最后,普通的参数如果也被加密或哈希,将会给数据分析工作带来很大的困扰,因为数据分析工作常常需要用到参数的明文。

因此,我们使用更通用的方案:Anti CSRF Token。Token应该作为一个“秘密”,为用户与服务器所共同持有,不能被第三者知晓。

Token的使用原则

  1. Token的生成一定要足够随机;
  2. 为了使用方便,可以允许在一个用户的有效生命周期内,在Token消耗掉之前都使用同一个Token。但是如果用户已经提交了表单,则这个Token已经消耗掉,应该再次重新生成一个新的Token。
  3. 如果Token保存在Cookie中,可以考虑生成多个有效的Token,已解决多页面共存的场景。
  4. 使用Token时应该注意Token的保密性。
  5. 使用Token时,应该尽量把Token放在表单中,把敏感操作由GET改为POST。