一次较为深刻的CSRF认识
0x00写在前面团队中的一枚好基友今天拿到了Ali的offer,表示羡慕不已,回来交流了一下心得,其中谈到了CSRF的问题,以前接触过CSRF,但是都浅尝则止了,作为Web狗来说,拓展思路很有必要啊,这次我也来做一回理论
0x00 写在前面
团队中的一枚好基友今天拿到了Ali的offer,表示羡慕不已,回来交流了一下心得,其中谈到了CSRF的问题,以前接触过CSRF,但是都浅尝则止了,作为Web狗来说,拓展思路很有必要啊,这次我也来做一回理论狗吧。下面是思路:
什么是CSRF 漏洞产生的原因 案例分析 如何防御它
0x01 什么是CSRF CSRF
(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。也就是可以这么说,攻击者盗用了你的身份,以你的名义发送恶意请求,比如删除某篇文章,发送某个消息等等,这一切都是基于你的名义来进行的操作。
2.下面这张图详细的描述了,CSRF的攻击过程
0x02原理的剖析 导致csrf漏洞原因 开发者编码导致的漏洞 Web浏览器对cookie的处理和http的验证有一定的缺陷 上图的的攻击思路
从上图可以看出,要完成一次CSRF攻击,受害者必须依次完成两个步骤:
1.登录受信任网站A,并在本地生成Cookie。
2.在不登出A的情况下,访问危险网站B。
看到这里,你也许会说:“如果我不满足以上两个条件中的一个,我就不会受到CSRF的攻击”。
是的,确实如此,但你不能保证以下情况不会发生:
1.你不能保证你登录了一个网站后,不再打开一个tab页面并访问另外的网站。
2.你不能保证你关闭浏览器了后,你本地的Cookie立刻过期,你上次的会话已经结束。(事实上,
关闭浏览器不能结束一个会话,但大多数人都会错误的认为关闭浏览器就等于退出登录/结束会话
了……)
3.上图中所谓的攻击网站,可能是一个存在其他漏洞的可信任的经常被人访问的网站。
0x03 案例分析
乌云上案例一大堆,选了两个。
GET类型的CSRF
这种类型的CSRF一般是由于程序员安全意识不强造成的。GET类型的CSRF利用非常简单,只需要一个HTTP请求。
<img src=http://neo.com/csrf.php?xx=11 />
访问含有这个标签后的页面后,成功向http://neo.com/csrf.php?xx=11发出了一次HTTP请求。如果将该网址替换为存在GET型CSRF的地址,就能完成攻击了。因为
标签会已Get形式请求第三方资源,所以浏览器会带上你网站的cookie发出Get请求。
乌云例子:搜狐微博应该是最后一枚高危GET型CSRF发微博蠕虫
POST类型的CSRF
这种类型的CSRF危害没有GET型的大,利用起来通常使用的是一个自动提交的表单
<form action=http://neo.com/csrf.php method=POST> <input type="text" name="xx" value="11" /> </form> <script> document.forms[0].submit(); </script>
<script> document.forms[0].submit(); </script>
访问该页面后,表单会自动提交,相当于模拟用户完成了一次POST操作。
乌云例子:图虫网利用csrf可劫持账号(小学6年级吓尿)
综合上面的分析来看,可以这么理解CSRF
CSRF攻击是源于WEB的隐式身份验证机制!WEB的身份验证机制虽然可以保证一个请求是来自于某个用户的浏览器,但却无法保证该请求是用户批准发送的!
0x04 如何防御?重点来了
目前有这几种方法
Post代替Get——显然没有什么卵用,攻击者可以在其他页面用js来模拟一个post请求,这个方法可以增加攻击难度。 校验 HTTP Referer
检测访问来路是否可信的最简单方法是,获得HTTP请求中的来路信息(即名为Referer的HTTP头—译者注)并且检查它来自站内还是来自一个远程的恶意页面:这是一个很好的解决方法,但是由于可以对服务器获得的请求来路进行欺骗以使得他们看起来合法,这种方法不能够有效防止攻击。
这是一个典型的http Referer的实现
<?php if(eregi(“www.playhack.net”, $_SERVER['HTTP_REFERER'])) { do_something(); } else { echo “非法请求!”; } ?>
但是显然也没有什么卵用,如果我喜欢,我可以给 referer 任何值。当然这个做法并不是毫无作用,起码可以防小白。
验证码
让用户在文本框中填写图片上的随机字符串,并且在提交表单后对其进行检测。当时我就惊呆了,这很影响用户体验啊,不过也是很有保证的做法!
Token
cookie中添加Token HTTP头中Token 一次性Token1. cookie添加Token
这可能是最简单的解决方案了,因为攻击者不能获得第三方的Cookie(理论上),所以表单中的数据也就构造失败了:>
<?php //构造加密的Cookie信息 $value = “DefenseSCRF”; setcookie(”cookie”, $value, time()+3600); ?>
在表单里增加Hash值,以认证这确实是用户发送的请求。
<?php $hash = md5($_COOKIE['cookie']); ?> <form method=”POST” action=”transfer.php”> <input type=”text” name=”toBankId”> <input type=”text” name=”money”> <input type=”hidden” name=”hash” value=”<?=$hash;?>”> <input type=”submit” name=”submit” value=”Submit”> </form>
然后在服务器端进行Hash值验证
<?php if(isset($_POST['check'])) { $hash = md5($_COOKIE['cookie']); if($_POST['check'] == $hash) { doJob(); } else { //... } else { //... } ?>
这个方法个人觉得已经可以杜绝99%的CSRF攻击了。
2. HTTP添加Token
这种方法也使用Token进行验证,和上一种方法不同的是,这里并不是把Token以参数的形式置于HTTP请求之中,而是把它放到HTTP头部自定义的属性中。
X-Requested-By: XMLHttpRequest
但是这个方法需要重写所有网站,把所有请求都改为XMLHttpRequest请求。
3. 一次性Token
cookie Token是所有表单都包含同一个伪随机数值,而一次性Token是每一个表单包含一个不同的伪随机值。
首先编写令牌生成函数gen_token:
<?php function gen_token() { //实际上单使用Rand()得出的随机数作为令牌,也是不安全的。 $token = md5(uniqid(rand(), true)); return $token; }
然后生成session token,以便在后面的检查中进行调用:
<?php function gen_stoken() { $pToken = ""; if($_SESSION[STOKEN_NAME] == $pToken) { //没有值,赋新值 $_SESSION[STOKEN_NAME] = gen_token(); } else { //继续使用旧的值 } } ?>
Web表单生成隐藏输入域的函数:
<?php function gen_input() { gen_stoken(); echo “<input type=\”hidden\” name=\”" . FTOKEN_NAME . “\” value=\”" . $_SESSION[STOKEN_NAME] . “\”> “; } ?>
WEB表单结构:
<?php session_start(); include(”functions.php”); ?> <form method=”POST” action=”transfer.php”> <input type=”text” name=”toBankId”> <input type=”text” name=”money”> <? gen_input(); ?> <input type=”submit” name=”submit” value=”Submit”> </FORM>
0x05 小结
在刺总的《白帽子讲Web安全》一书中提到,CSRF能够攻击成功的本质是:重要的操作的所有参数都是可以被攻击者猜测到的。然而只要防止攻击者能够成功的构造一个伪造请求,就可以杜绝攻击了! Token验证是当下最使用于防止CSRF的一种有效手段,作为搞安全的人来说,清楚Token的使用和设计原则是很用必要的。这篇文章写了一个晚上加几个点,更加的让我感觉到,网络安全海洋的广阔,提高自己的思维活跃度刻不容缓啊。