概述

介绍下列内容:

  • 什么是CSRF攻击
  • 漏洞举例
  • 常见的防御方式

什么是CSRF攻击

狭义的CSRF攻击

狭义的CSRF攻击就是指让Client在被Server认证的情况下去访问精心设计的恶意链接完成攻击。

  1. 因为HTTP是无状态的协议,即Server不知道每个请求来源于哪个用户。
  2. 所以服务器有时会在Client存储可以标识用户身份的信息。
  3. 在Client发送请求的时候附上用户身份信息保证Server可以识别出用户。
  4. 如果此时用户点击了一个恶意链接,且恶意链接会发送一个恶意请求到Server。
  5. 一般情况下会因为不能识别用户身份而而被拒绝。
  6. 但是如果此时Client是刚刚被服务端认证过的,则发送恶意请求的同时也会附上刚刚认证的身份信息。
  7. Server收到恶意请求后发现Client是认证过的,于是将其识别为合法请求。
  8. 攻击者就完成了一次CSRF攻击。

广义的CSRF攻击

通过一些手段猜测出请求参数以及用户认证信息(如果需要认证的话),这样Server就会将其识别为合理请求。

例如某网站转账的URL为https://www.bank.com/transfer?user=小明&money=1000&for=小红,代表小明转账给小红1000元。那么攻击者就可以构造出这个URLhttps://www.bank.com/transfer?user=小明&money=9999999999&for=小李直接掏空小明的钱包。

漏洞举例

  1. 挖洞经验 | Facebook的Gmail验证机制存在的CSRF漏洞 | CSRF Token缺少验证措施。
  2. Facebook上一个有趣的CSRF | 授权过程中缺少state参数。
  3. 挖洞经验 | 利用Instagram版权功能构造CSRF漏洞删除其他用户文件 | 重要的参数直接写在GET请求里。

常见的防御方式

验证码

在执行一些重要请求之前通过验证码的方式验证用户身份,例如短信验证码,图片验证码等。所以你会发现许多网站即使在登录状态先执行一些操作也需要验证。

同源检测

根据HTTP协议,在HTTP头中有一个字段叫Referer,记录了该HTTP请求的来源地址。 对于Ajax请求,图片和script等资源请求,Referer为发起请求的页面地址。对于页面跳转,Referer为打开页面历史记录的前一个页面地址。因此我们使用Referer中链接的Origin部分可以得知请求的来源域名。

这种方法并非万无一失,Referer的值是由浏览器提供的,虽然HTTP协议上有明确的要求,但是每个浏览器对于Referer的具体实现可能有差别,并不能保证浏览器自身没有安全漏洞。使用验证 Referer 值的方法,就是把安全性都依赖于第三方(即浏览器)来保障,从理论上来讲,这样并不是很安全。在部分情况下,攻击者可以隐藏,甚至修改自己请求的Referer。

同源验证是一个相对简单的防范方法,能够防范绝大多数的CSRF攻击。但这并不是万无一失的,对于安全性要求较高,或者有较多用户输入内容的网站,我们就要对关键的接口做额外的防护措施。

CSRF Token

CSRF攻击之所以能够成功,是因为服务器误把攻击者发送的请求当成了用户自己的请求。那么我们可以要求所有的用户请求都携带一个CSRF攻击者无法获取到的Token。服务器通过校验请求是否携带正确的Token,来把正常的请求和攻击的请求区分开,也可以防范CSRF的攻击。

通过一套合适的规则(一般来说这套规则要便于分布式校验)来生成随机的Token并跟随form表单一起提交,由于HTTPS在传输过程中是加密的,所以攻击者无法截获Token,前提是站点没有XSS漏洞。

但是此方法的实现比较复杂,需要给每一个页面都写入Token(前端无法使用纯静态页面),每一个Form及Ajax请求都携带这个Token,后端对每一个接口都进行校验,并保证页面Token及请求Token一致。这就使得这个防护策略不能在通用的拦截上统一拦截处理,而需要每一个页面和接口都添加对应的输出和校验。这种方法工作量巨大,且有可能遗漏。

双重Cookie验证

在会话中存储CSRF Token比较繁琐,而且不能在通用的拦截上统一处理所有的接口。

那么另一种防御措施是使用双重提交Cookie。利用CSRF攻击不能获取到用户Cookie的特点,我们可以要求Ajax和表单请求携带一个Cookie中的值。

双重Cookie采用以下流程:

  • 在用户访问网站页面时,向请求域名注入一个Cookie,内容为随机字符串(例如csrfcookie=v8g9e4ksfhw)。
  • 在前端向后端发起请求时,取出Cookie,并添加到URL的参数中(接上例POST https://www.a.com/comment?csrfcookie=v8g9e4ksfhw)。
  • 后端接口验证Cookie中的字段与URL参数中的字段是否一致,不一致则拒绝。

攻击者可以获取到URL参数中的Token值,但是无法获取到Cookie中的Token值,因此攻击者无法构造出恶意链接。

你可能会想既然我已经截取到了URL参数中的Token值,那么攻击者也就可以猜测出Cookie中的Token值。对,没错,因为上面的流程中URL参数和Cookie中存储的是相同的值,那么如果我对Cookie中的值进行加密呢?那么攻击者就无法推测出Cookie的内容了。

此方法相对于CSRF Token就简单了许多。可以直接通过前后端拦截的的方法自动化实现。后端校验也更加方便,只需进行请求中字段的对比,而不需要再进行查询和存储Token。

当然,此方法并没有大规模应用,其在大型网站上的安全性还是没有CSRF Token高,因为任何跨域都会导致前端无法获取Cookie中的字段(包括子域名之间)。

参考文章