为什么CSRF Token能够放在Cookie中

作者:h1nt 来源:blog.csdn.net 更新时间:2023-05-25 21:55

0x01 前言

在某次测试中注意到网站的CSRF的TOKEN放在Cookie中,感觉和之前的一些知识起了冲突。遂查了些资料整理明白了,并记录于此文。

 

0x02 CSRF与CSRF Token

相信有很多师傅的知识都是通过道哥的《白帽子讲WEB安全》中习得的,里面有一个删除搜狐博客的例子,从大体来说可以概括如下:

在这里插入图片描述

对于防御方法,很多博文中也有介绍,就是通过种种形式添加CSRF Token

 

0x03 疑问&解答

然后呢在某次测试中,抓到的包如下

在这里插入图片描述

 

可以看到有多个csrftoken的字样,当时我就纳闷了,因为攻击者的CSRF攻击(大多数)恰恰就是利用的Cookie,那么csrftoken写在这个地方有用吗?

 

为了验证我的想法,我用Burp的CSRF工具进行了一个小小的测试:

在这里插入图片描述

 

在界面点击按钮,可以看到发起了请求

在这里插入图片描述

 

之后看到拿到的数据包,可以看到是没有Cookie信息的,理所当然不能通过网站的校验,自然网站本身也是安全的。

在这里插入图片描述

 

那这个我之前学到的知识冲突在哪呢?我们常见的CSRF Token往往是放在URL或者Form表单中的,没太意识到原来CSRF Token也能放在Cookie中,那么这种方式能够防御的原理是啥?

 

在一番查阅资料后,我发现主要使用了两种技术,能够使得站点能够把Token放在Cookie中,参考链接

 

其一是Chrome中Cookie的SameSite字段,这里摘抄一段,如下



防止CSRF攻击的办法已经有上面的预防措施。为了从源头上解决这个问题,Google起草了一份草案来改进HTTP协议,那就是为Set-Cookie响应头新增Samesite属性,它用来标明这个 Cookie是个“同站 Cookie”,同站Cookie只能作为第一方Cookie,不能作为第三方Cookie,Samesite 有两个属性值,分别是 Strict 和 Lax,下面分别讲解:

Samesite=Strict

这种称为严格模式,表明这个 Cookie 在任何情况下都不可能作为第三方 Cookie,绝无例外。比如说 b.com 设置了如下 Cookie:

Set-Cookie: foo=1; Samesite=Strict
Set-Cookie: bar=2; Samesite=Lax
Set-Cookie: baz=3
 
  • 1
  • 2
  • 3

我们在 a.com 下发起对 b.com 的任意请求,foo 这个 Cookie 都不会被包含在 Cookie 请求头中,但 bar 会。举个实际的例子就是,假如淘宝网站用来识别用户登录与否的 Cookie 被设置成了 Samesite=Strict,那么用户从百度搜索页面甚至天猫页面的链接点击进入淘宝后,淘宝都不会是登录状态,因为淘宝的服务器不会接受到那个 Cookie,其它网站发起的对淘宝的任意请求都不会带上那个 Cookie。

Samesite=Lax

这种称为宽松模式,比 Strict 放宽了点限制:假如这个请求是这种请求(改变了当前页面或者打开了新页面)且同时是个GET请求,则这个Cookie可以作为第三方Cookie。比如说 b.com设置了如下Cookie:

Set-Cookie: foo=1; Samesite=Strict
Set-Cookie: bar=2; Samesite=Lax
Set-Cookie: baz=3
 
 

当用户从 a.com 点击链接进入 b.com 时,foo 这个 Cookie 不会被包含在 Cookie 请求头中,但 bar 和 baz 会,也就是说用户在不同网站之间通过链接跳转是不受影响了。但假如这个请求是从 a.com 发起的对 b.com 的异步请求,或者页面跳转是通过表单的 post 提交触发的,则bar也不会发送。



 

而在我们的案例中,打开开发者界面,可以看到我们的Cookie是被设置了SameSite属性的,且其值为Lax。我们的数据包是个POST请求,自然也就不会带上Cookie了

在这里插入图片描述

 

为了进一步验证,我们可以将SameSite属性设置成None,同时添加Secure属性,如下

在这里插入图片描述

 

再次使用CSRF的Poc,可以发现可以携带Cookie了

在这里插入图片描述

 

我们需要注意的是,这个SameSite属性是Google家的东西,也就是可能存在别的浏览器不兼容的情况,所以我们还需要别的方案作辅助验证

 

刚好这个站点也有这个方案,名字是双重Cookie验证,这里继续摘抄



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

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

双重Cookie采用以下流程:

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

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

当然,此方法并没有大规模应用,其在大型网站上的安全性还是没有CSRF Token高,原因我们举例进行说明。

由于任何跨域都会导致前端无法获取Cookie中的字段(包括子域名之间),于是发生了如下情况:

  • 如果用户访问的网站为www.a.com,而后端的api域名为api.a.com。那么在www.a.com下,前端拿不到api.a.com的Cookie,也就无法完成双重Cookie认证。
  • 于是这个认证Cookie必须被种在a.com下,这样每个子域都可以访问。
  • 任何一个子域都可以修改a.com下的Cookie。
  • 某个子域名存在漏洞被XSS攻击(例如upload.a.com)。虽然这个子域下并没有什么值得窃取的信息。但攻击者修改了a.com下的Cookie。
  • 攻击者可以直接使用自己配置的Cookie,对XSS中招的用户再向www.a.com下,发起CSRF攻击。


 

在这个站点中,如下

在这里插入图片描述

 

也就是说,就算我们SameSite属性由于种种原因失效了,浏览器访问的时候带上了Cookie,我们依旧无法完成CSRF攻击。因为发起请求的HTTP HEADER对攻击者来说是不可控的,服务端只需要比较头部的自定义属性(即csrf-token)和Cookie中值是否一致即可