CSRF(Cross-site request forgery跨站请求伪造,也被称成为”one click attack”或者session riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。
1.攻击原理:
CSRF攻击原理比较简单,如图1所示。其中Web A为存在CSRF漏洞的网站,Web B为攻击者构建的恶意网站,User C为Web A网站的合法用户。
1.用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;
2.在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;
3.用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;
4.网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
5.浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是
由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。
2.攻击举例
银行网站A,它以GET请求来完成银行转账的操作,如:http://www.mybank.com/Transfer.php?toBankId=11&money=1000危险网站B,它里面有一段HTML代码如下:<img src=http://www.mybank.com/Transfer.php?toBankId=11&money=1000>首先,你登录了银行网站A,然后访问危险网站B,噢,这时你会发现你的银行账户少了1000块……为什么会这样呢?原因是银行网站A违反了HTTP规范,使用GET请求更新资源。在访问危险网站B的之前,你已经登录了银行网站A,而B中的<img>以GET的方式请求第三方资源(这里的第三方就是指银行网站了,原本这是一个合法的请求,但这里被不法分子利用了),所以你的浏览器会带上你的银行网站A的Cookie发出Get请求,去获取资源”http://www.mybank.com/Transfer.php?toBankId=11&money=1000”,结果银行网站服务器收到请求后,认为这是一个更新资源操作(转账操作),所以就立刻进行转账操作……同样的道理,即使是用post依然会被轻易利用,在此不再冗余举例
4.漏洞防御:
增加二次验证,在表单提交之前在此输入验证码或者其他标识,如12306的校验过程
验证HTTP REFERER,HTTP源地址用来标识请求的源地址,即此次页面从哪发起,校验的代码如下
// 从 HTTP 头中取得 Referer 值 `</pre>
<pre>` String referer=request.getHeader("Referer"); `</pre>
<pre>` // 判断 Referer 是否以 bank.example 开头 `</pre>
<pre>` if((referer!=null) &&(referer.trim().startsWith("bank.example"))){ `</pre>
<pre>` chain.doFilter(request, response); `</pre>
<pre>` }else{ `</pre>
<pre>` request.getRequestDispatcher("error.jsp").forward(request,response); `</pre>
<pre>` }`</pre>
</td>
</tr>
</tbody>
</table>
</div>
<span style="color: #333333;">在实际生成环境中,因为代理服务器和防火墙的原因,源地址常常会被屏蔽掉,所以此种方法慎用,增加随机数校验TOKEN,其核心思想是客户端的请求时的随机数与存在后台的数据进行对比,前台的随机数常用隐藏的input中、自定义header中、cookie中,服务器端的随机数一般存在SESSION中,由此衍生出来的防御方法如下:前台的基础是浏览器的同源策略,即第三方的页面无法获取本页面的相应信息
</span>
<span style="color: #333333;">**基于隐藏input的代码
**</span>
<div>
<table style="border-collapse: collapse; background: #fbd4b4;" border="0"><colgroup> <col style="width: 590px;" /></colgroup>
<tbody valign="top">
<tr>
<td style="padding-left: 7px; padding-right: 7px; border: solid 0.5pt;">
<pre>`<form method="POST" action="transfer.php"> <input type="text" name="toBankId"> <input type="text" name="money"> <input type="hidden" name="hash" value="****"> <input type="submit" name="submit" value="Submit"> `</pre>
<pre>` </form>`</pre>
</td>
</tr>
</tbody>
</table>
</div>
<span style="color: #333333;">**基于HASHCOOKIE的代码
**</span>
<div>
<table style="border-collapse: collapse; background: #fbd4b4;" border="0"><colgroup> <col style="width: 590px;" /></colgroup>
<tbody valign="top">
<tr>
<td style="padding-left: 7px; padding-right: 7px; border: solid 0.5pt;">
<pre>`$token = md5(uniqid(rand(), TRUE)); `</pre>
<pre>`$_SESSION['token'] = $token; `</pre>
<pre>`$_SESSION['token_time'] = time()`</pre>
</td>
</tr>
</tbody>
</table>
</div>
<span style="color: #333333;">**基于自定义header的条件
**</span>
<div>
<table style="border-collapse: collapse; background: #fbd4b4;" border="0"><colgroup> <col style="width: 590px;" /></colgroup>
<tbody valign="top">
<tr>
<td style="padding-left: 7px; padding-right: 7px; border: solid 0.5pt;">
<pre>`dojo.xhr = function(method,args,hasBody) { `</pre>
<pre>` // 确保 header 对象存在 `</pre>
<pre>` args.headers = args.header || {}; `</pre>
<pre>` tokenValue = '<=request.getSession(false).getAttribute("csrftoken")%>'; `</pre>
<pre>` var token = dojo.getObject("tokenValue"); `</pre>
<pre>` // 把 csrftoken 属性放到头中 `</pre>
<pre>` args.headers["csrftoken"] = (token) ? token : " "; `</pre>
<pre>` return plainXhr(method,args,hasBody); `</pre>
<pre>`}
4.总结与思考:
其中起到关键因素因素是浏览器的同源策略,如果没有看懂的小伙盘,可以研究下这块
目前最好的解决方案依然停留在客户端与服务端的对比校验,随机数有服务端发起,客户端在数据流中只负责带上,服务端自行校验
5.引用:
http://www.freebuf.com/articles/web/11840.html
http://www.cnblogs.com/hyddd/archive/2009/04/09/1432744.html
《白帽子讲WEB安全》