前言
全称跨站请求伪造(Cross site request forgery), 尽管它听起来与XSS(跨站脚本攻击),但它与XSS非常不同。
然而事实是,CSRF与XSS具有很大的相似性和关联性。
一句话来说,就是可以利用某站点存在的XSS漏洞来进行CSRF攻击。
CSRF的核心过程:
攻击者利用站点B的XSS漏洞上传了恶意脚本,用户进入站点B时脚本执行,脚本内容是:向用户经常浏览的网站A发起请求,此时如果用户恰好登录过站点B,在浏览器中留有未过期的cookie,那么脚本在请求A时就会自动带上浏览器中的cookie,执行一次未经用户授权的请求。
CSRF实现的前提:
- 攻击者熟知访问站点A的用户具有访问站点B的浏览习惯(或者攻击者通过某种方式引诱用户在站点A中点击了站点B链接)
- 站点B具有XSS漏洞,没有对攻击者上传的内容做HTML转义
- 站点A允许cookie被访问
- 站点A服务端安全措施较低,没有对请求的referer、origin进行检验。
正文
一个栗子:
我10点钟时使用浏览器登录了某银行网站bank.com,那么浏览器中肯定有带有我信息的cookie,假设这个网站把我的身份凭证放在了cookie中。ok,10点半时,同一个浏览器,我想寻求一些帮助,打开了该银行的论坛网站banktalk.com。该网站聚集大量银行A的用户,我打开了某篇具有较高访问量的帖子,然后我收到一条短信:
尊敬的用户xx,您在修改账户密码,本次验证码是xxx,如果这不是您本人的意愿,请不要将验证码告诉他人,并尽快删除此短信。
WTF,我没有这个操作啊,我的账户被盗了吗?那么到底发生了什么?
首先,论坛banktalk.com的这个帖子内有人上传了带恶意脚本的内容,它的内容是一个form表单,在执行时发送一个POST请求。表单内容如下:
<form id="changepass" action="https://bank.com/user/changepassword" method="POST" display="none"><input type="text" name="new_pass" value="123456" />
</form>
<script>var form = document.form["changepass"];form.submit();
</script>
当我打开帖子时,脚本自动执行,向bank.com发送了修改密码的请求,并且携带了浏览器中bank.com域下的cookie信息,其中就包含我的身份凭证,所以这次请求得以“成功”,虽然被验证码这一步挡住了。但有其他太多操作都不需要验证码的,那岂不是完蛋?
如果是GET请求,那就更简单了,脚本可隐藏在img标签中,类似这样:
<img src="http://bank.com/withdraw?amount=10000&for=hacker" >
解决方案
假如我是受害网站,我能做什么?
- 服务端设置部分敏感或全部cookie的httponly属性, 不允许JS获取cookie。(这样脚本就无法获取我的cookie)
- 服务端对每一个请求进行csrftoken验证,token由服务端生成(比如对约定key和UID+时间戳的对称加密串),存放在网站的页面中,前端开发者需要遍历DOM树,将token置于所有的a和form标签中,对于ajax请求,需要开发者手动添加,GET和POST请求的添加方式有一点差别。(这样一来,存在XSS漏洞的网站拿不到token,自然请求失败)
同源策略,对请求的referer和origin进行检验,必须是与网站域名一致或在服务端域名白名单内。(referer和origin由浏览器设置,恶意脚本无法设置请求的referer以及origin)- 对于某些重要的请求(操作),比如修改密码等,要求短信验证码或二次输入密码验证码。
对于第3点的解释,书中提到了这一点。但是根据博主的搜索验证,这一点不可取,原因有三:
- 旧版IE浏览器在某些情况下会丢失referer,甚至是origin;或某些浏览器存在这方面的漏洞,导致referer缺失甚至攻击者可以修改referer,或未完全遵循HTTP协议定义,
- JS脚本使用WinHttp.WinHttpRequest对象可以伪造Header(referer和origin不再可信)
- 误伤来自搜索引擎的请求
本文来自博主看书以及仔细搜索查验后的总结,如有瑕疵或错误,欢迎读者纠正。