概述
介绍下列内容:
- 什么是 CSRF 攻击
- 漏洞举例
- 常见的防御方式
什么是CSRF攻击
狭义的CSRF攻击
狭义的CSRF攻击就是指让Client在被Server认证的情况下去访问精心设计的恶意链接完成攻击。
- 因为 HTTP 是无状态的协议,即 Server 不知道每个请求来源于哪个用户。
- 所以服务器有时会在Client存储可以标识用户身份的信息。
- 在Client发送请求的时候附上用户身份信息保证Server可以识别出用户。
- 如果此时用户点击了一个恶意链接,且恶意链接会发送一个恶意请求到 Server。
- 一般情况下会因为不能识别用户身份而而被拒绝。
- 但是如果此时 Client 是刚刚被服务端认证过的,则发送恶意请求的同时也会附上刚刚认证的身份信息。
- Server 收到恶意请求后发现Client是认证过的,于是将其识别为合法请求。
- 攻击者就完成了一次 CSRF 攻击。
广义的CSRF攻击
通过一些手段猜测出请求参数以及用户认证信息(如果需要认证的话),这样Server就会将其识别为合理请求。
例如某网站转账的URL为https://www.bank.com/transfer?user=小明&money=1000&for=小红
,代表小明转账给小红 1000 元。那么攻击者就可以构造出这个URL—https://www.bank.com/transfer?user=小明&money=9999999999&for=
小李直接掏空小明的钱。
漏洞举例
- 挖洞经验 | Facebook的Gmail验证机制存在的CSRF漏洞 | CSRF Token缺少验证措施
- Facebook上一个有趣的 CSRF | 授权过程中缺少 state 参数
- 挖洞经验 | 利用 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中的字段(包括子域名之间)。