vlambda博客
学习文章列表

最后一次,搞懂CSRF攻击!

完整阅读本文大约需要5分钟。

开始阅读之前,先上一道面试题:

CSRF攻击和XSS攻击之间,有什么联系?

什么是CSRF攻击

CSRF攻击即Cross-site request forgery,跨站请求伪造,直白来说就是恶意网站伪装成用户,向被害网站发起操作请求。

为了方便理解,做了一张图,攻击流程如下:

  1. 用户登录受害网站,浏览器把获取的身份凭证保存在本地cookie中;
  2. 用户被诱导打开黑客网站,黑客网站向受害网站服务器发起一个恶意请求,由于cookie的取用规则,这时浏览器会自动带上第一步中的身份凭证;
  3. 受害网站服务端对恶意请求校验,发现有身份凭证,恶意请求被成功受理;

如果黑客的操作是将用户的钱转到自己的账户,那么这时,他已经卷款跑路了。

如何发起CSRF攻击

  1. 诱导用户跳转到黑客网站,网站的HTML中有一个自动提交的隐藏表单,只要用户打开页面,就会发起转账请求;
 <form action="http://bank/transfer" method=POST>
    <input type="hidden" name="account" value="user" />
    <input type="hidden" name="amount" value="10000" />
    <input type="hidden" name="for" value="hacker" />
</form>
<script> document.forms[0].submit(); </script> 
  1. 在受害网站的评论区放置一个a标签,点击跳转时发起伪造请求;
<a href="http://bank/transfer?amount=10000&for=hacker" taget="_blank">
  大八卦
<a/>
  1. 在受害网站的评论区发表伪装的图片,实际是一个恶意请求,浏览过评论区的用户都会被黑客攻击;
![风景图](http://bank/transfer?amount=10000&for=hacker)

怎么预防CSRF攻击

CSRF攻击有两个特点:

  1. CSRF攻击的来源通常是黑客网站(外域),因为更好控制;
  2. CSRF攻击无法获取用户的身份凭证,只能是冒用;

所以可以想到以下几个方法:

检验请求来源

HTTP 请求头中有两个字段会标识请求的来源:originreferer。这两个字段不受前端控制,会诚实地告诉服务器请求来自哪里。

origin是请求来源的域名部分,但是在302重定向、以及IE11上不会显示。

所以还需要referer辅助,它是一个完整的url路径,但可靠性不如origin

SameSite cookie

CSRF攻击能成功,正是所有向受害网站发起的请求,都会被自动带上cookie。

Chrome意识到这个问题后起草了一份协议:向浏览器注入cookie时,开发者可以标注哪些请求才会带上。

被标注为strict的cookie只有本域的请求才能带上。

被标注为lax的cookie在跳转到新页面时可以带上。因为如果全部设置为strict,在百度搜索并打开淘宝,默认是没有登录的,用户体验会很差。

CSRF Token

在本站发起的请求中,加一个攻击者无法获取的token,也可以区别出正常请求和恶意请求。这个token和浏览器自动携带的cookie不一样,是需要前端手动带上的。

但这种方案对服务器压力较大,需要维护一个session对收到的token做校验。

双重cookie

相较于与token,双重cookie不需要服务器做额外扩容。只需要在请求中加一个额外的字段,其值和cookie一致。因为上文提到过,攻击者没法获取到cookie,只是在发起请求时会携带。

在服务端收到请求时,如果没有和cookie值一样的额外字段,就可以认为是来自恶意网站。

其他通用方式

CSRF攻击大多来自第三方网站,但就像上文提到过的链接跳转和图片伪装,本站的请求也可能造成威胁。所以我们还需要对UGC内容做一些过滤。

  1. 当前用户打开其他用户填写的链接时,需告知风险;
  2. 不直接使用用户上传的图片,先在自己的服务器转存;

结语

回到开头提出的问题:

XSS攻击的核心是注入代码,获取用户信息;CSRF攻击的核心是借助身份凭证,伪装用户发起操作请求。

两者往往是前后出现的:黑客会先通过XSS攻击获取到用户的身份凭证,上传到黑客网站,然后就可以利用它伪装成用户,发起操作请求。