当前位置: 首页 > 知识库问答 >
问题:

为什么在cookie中放置CSRF预防令牌很常见?

乐宜民
2023-03-14

我正试图了解CSRF的整个问题和预防它的适当方法。(我阅读、理解和同意的参考资料:OWASP CSRF预防备忘单、关于CSRF的问题。)

根据我的理解,CSRF的漏洞是由以下假设引起的:(从Web服务器的角度来看)传入HTTP请求中的有效会话cookie反映了经过身份验证的用户的意愿。但是源域的所有cookie都是由浏览器神奇地附加到请求上的,因此服务器实际上可以从请求中存在有效会话cookie推断出的全部信息就是该请求来自具有经过身份验证的会话的浏览器;它不能进一步假设在该浏览器中运行的代码,或者它是否真的反映了用户的意愿。防止这种情况的方法是在请求中包含附加的身份验证信息(“CSRF令牌”),该信息通过浏览器自动cookie处理以外的其他方法来携带。那么,松散地说,会话cookie对用户/浏览器进行身份验证,CSRF令牌对浏览器中运行的代码进行身份验证。

因此,简而言之,如果您正在使用会话cookie来验证web应用程序的用户,那么您还应该为每个响应添加一个CSRF令牌,并且在每个(变异的)请求中都需要一个匹配的CSRF令牌。然后CSRF令牌进行从服务器到浏览器再到服务器的往返,向服务器证明发出请求的页面是由该服务器批准的(甚至是由该服务器生成的)。

关于我的问题,这个问题是关于CSRF令牌在往返过程中使用的特定传输方法。

通常(例如在AngularJS、Django、Rails中)将CSRF令牌作为cookie(即在Set-Cookie头中)从服务器发送到客户机,然后让客户机中的Javascript将其从cookie中刮出并附加为单独的XSRF-TOKEN头以发送回服务器。

(另一种方法是例如Express推荐的方法,其中服务器生成的CSRF令牌通过服务器端模板扩展包含在响应主体中,直接附加到将其提供回服务器的代码/标记上,例如作为隐藏的表单输入。该示例是一种更像Web1.0的操作方式,但将很好地推广到更重JS的客户端。)

为什么使用Set-Cookie作为CSRF令牌的下游传输是如此普遍/为什么这是一个好主意?我想所有这些框架的作者都仔细考虑了他们的选项,没有弄错这一点。但乍一看,使用cookie来解决cookie的设计限制似乎很愚蠢。事实上,如果您使用cookie作为往返传输(set-cookie:header downdown用于服务器将CSRF令牌通知浏览器,而cookie:header上游用于浏览器将其返回服务器),则会重新引入您试图修复的漏洞。

我意识到上面的框架没有在CSRF令牌的整个往返过程中使用cookie;它们在下游使用Set-Cookie,然后在上游使用其他东西(例如X-CSRF-Token头),这确实可以关闭漏洞。但即使使用Set-Cookie作为下游传输也有潜在的误导性和危险性;浏览器现在将CSRF令牌附加到每个请求,包括真正的恶意XSRF请求;最好的情况下,这会使请求比它需要的更大,而最坏的情况下,一些善意但错误的服务器代码可能会试图使用它,这将是非常糟糕的。而且,由于CSRF令牌的实际目标接收方是客户端Javascript,这意味着这个cookie不能仅使用HTTP来保护。所以在Set-Cookie头中向下游发送CSRF令牌对我来说似乎不太理想。

共有2个答案

姬俊驰
2023-03-14

使用cookie向客户端提供CSRF令牌不允许成功攻击,因为攻击者无法读取cookie的值,因此无法将其放置在服务器端CSRF验证所要求的位置。

攻击者将能够在请求头中同时使用身份验证令牌cookie和CSRF cookie向服务器发出请求。但是服务器不是在请求头中寻找CSRF令牌作为cookie,而是在请求的有效负载中寻找。即使攻击者知道将CSRF令牌放入有效负载的位置,他们也必须读取其值才能将其放入。但浏览器的跨源策略阻止从目标网站读取任何cookie值。

同样的逻辑不适用于身份验证令牌cookie,因为服务器希望它出现在请求头中,而攻击者不需要做任何特别的事情来将它放在那里。

杨景山
2023-03-14

一个很好的原因(您已经提到了)是,一旦收到CSRF cookie,它就可以在整个应用程序中以客户端脚本的形式使用,既可以在常规形式中使用,也可以在AJAX post中使用。这在像AngularJS所使用的JavaScript应用程序(使用AngularJS并不要求应用程序是单个页面应用程序,因此当状态需要在CSRF值通常无法在浏览器中持久存在的不同页面请求之间流动时,这将是有用的)中是有意义的(使用AngularJS并不要求应用程序是单个页面应用程序)。

在典型应用程序中考虑以下场景和过程,了解您描述的每种方法的一些利弊。这些都是基于同步器令牌模式的。

  1. 用户成功登录
  2. 服务器发出身份验证Cookie。
  3. 用户单击以导航到窗体。
  4. 如果尚未为此会话生成CSRF令牌,则服务器将生成CSRF令牌,根据用户会话存储它,并将其输出到隐藏字段。
  5. 用户提交表单。
  6. 服务器检查隐藏字段与会话存储令牌匹配。

优点:

  • 实现简单。
  • 使用Ajax。
  • 使用窗体。
  • cookie实际上只能是HTTP。

缺点:

  • 所有表单都必须以HTML形式输出隐藏字段。
  • 任何AJAX文章还必须包含该值。
  • 页面必须事先知道它需要CSRF令牌,以便可以将其包含在页面内容中,因此所有页面都必须在某个位置包含令牌值,这可能会使实现大型站点时耗费时间。
  1. 用户成功登录。
  2. 服务器发出身份验证Cookie。
  3. 用户单击以导航到窗体。
  4. 页面加载到浏览器中,然后发出一个AJAX请求来检索CSRF令牌。
  5. 服务器生成CSRF令牌(如果尚未为会话生成),针对用户会话存储它,并将其输出到报头。
  6. 用户提交表单(通过隐藏字段发送令牌)。
  7. 服务器检查隐藏字段与会话存储令牌匹配。

优点:

  • 使用Ajax。
  • cookie只能是HTTP。

缺点:

  • 在没有AJAX请求以获取头值的情况下无法工作。
  • 所有表单都必须将值动态添加到其HTML中。
  • 任何AJAX文章还必须包含该值。
  • 页面必须首先发出AJAX请求以获取CSRF令牌,因此这将意味着每次都要进行额外的往返。
  • 不妨将令牌输出到页面,这样可以保存额外的请求。
  1. 用户成功登录。
  2. 服务器发出身份验证Cookie。
  3. 用户单击以导航到窗体。
  4. 如果尚未为此会话生成CSRF令牌,则服务器将生成CSRF令牌,根据用户会话存储该令牌,并将其输出到页面内容的某个位置。
  5. 用户通过AJAX提交表单(令牌通过头发送)。
  6. 服务器检查自定义标头与会话存储的令牌匹配。

优点:

  • 使用Ajax。
  • cookie只能是HTTP。

缺点:

  • 不适用于窗体。
  • 所有AJAX文章都必须包含标题。
  1. 用户成功登录。
  2. 服务器发出身份验证Cookie。
  3. 用户单击以导航到窗体。
  4. 页面加载到浏览器中,然后发出一个AJAX请求来检索CSRF令牌。
  5. 服务器生成CSRF令牌(如果尚未为会话生成),针对用户会话存储它,并将其输出到报头。
  6. 用户通过AJAX提交表单(令牌通过头发送)。
  7. 服务器检查自定义标头与会话存储的令牌匹配。

优点:

  • 使用Ajax。
  • cookie只能是HTTP。

缺点:

  • 不适用于窗体。
  • 所有AJAX文章还必须包含该值。
  • 页面必须首先发出AJAX请求以获取CRSF令牌,因此这将意味着每次都要进行额外的往返。
  1. 用户成功登录。
  2. 服务器发出身份验证Cookie。
  3. 用户单击以导航到窗体。
  4. 服务器生成CSRF令牌,根据用户会话存储该令牌,并将其输出到cookie。
  5. 用户通过AJAX或HTML表单提交表单。
  6. 服务器检查自定义标头(或隐藏的表单字段)是否与会话存储的令牌匹配。
  7. 浏览器中的Cookie可用于其他AJAX和表单请求,而无需对服务器进行其他请求以检索CSRF令牌。

优点:

  • 实现简单。
  • 使用Ajax。
  • 使用窗体。
  • 不一定需要AJAX请求来获取cookie值。任何HTTP请求都可以检索它,并且可以通过JavaScript将它附加到所有Forms/Ajax请求。
  • 一旦检索到CSRF令牌(因为它存储在cookie中),就可以重用该值,而无需其他请求。

缺点:

  • 所有表单都必须将值动态添加到其HTML中。
  • 任何AJAX文章还必须包含该值。
  • 将为每个请求(即不涉及CSRF过程的图像、CSS、JS等的所有GET)提交cookie以增加请求大小。
  • cookie不能仅为HTTP。

因此,cookie方法是相当动态的,提供了一种检索cookie值(任何HTTP请求)并使用它的简单方法(JS可以自动将该值添加到任何表单中,并且可以在AJAX请求中作为头或表单值使用)。一旦收到会话的CSRF令牌,就不需要重新生成它,因为利用CSRF漏洞的攻击者无法检索此令牌。如果恶意用户试图以上述任何方法读取用户的CSRF令牌,则相同来源策略将阻止此操作。如果恶意用户试图检索CSRF令牌服务器端(例如,通过curl),则此令牌将不会与受攻击者的身份验证会话cookie在请求中丢失的同一用户帐户相关联(它将是攻击者的-因此它不会与受攻击者的会话相关联)。

除了同步器令牌模式之外,还有双重提交Cookie CSRF预防方法,当然,它使用Cookie存储一种CSRF令牌。这更容易实现,因为它不需要CSRF令牌的任何服务器端状态。使用此方法时,CSRF令牌实际上可以是标准的身份验证cookie,并且该值与请求一样通过cookie提交,但该值也会在隐藏字段或头中重复,攻击者无法复制这些字段或头,因为他们无法首先读取该值。但是,建议选择另一个cookie,而不是身份验证cookie,这样身份验证cookie可以通过标记为httponly来保护安全。因此,这是使用基于cookie的方法进行CSRF预防的另一个常见原因。

 类似资料:
  • 我想从Stormpath帖子中对JWT令牌和CSRF提出疑问,这些令牌和CSRF解释了将JWT存储在localStorage或Cookie中的优缺点。 [...] 如果您使用JS从cookie中读取值,这意味着您不能在cookie上设置Httponly标志,因此现在站点上的任何JS都可以读取它,从而使其与在localStorage中存储内容的安全级别完全相同。 我试图理解为什么他们建议将xsrfT

  • 我对CakePHP(v3.7)比较陌生。我有一个应用程序,其中出现了“缺少Csrf令牌Cookie”错误。 在pplication.php,我有: 表单页面有一个隐藏的表单元素,其中包含_csrfToken。我不明白为什么没有在邮件上找到它? 深入研究后,我发现在CsrfProttionMiddleware.php中,下面的_validateToken()函数的行为如下: $cookie为空(没有

  • 我刚刚读了一篇文章 https://hasura . io/blog/best-of-practices-of-use-jwt-with-graph QL/ 总之,他们建议将JWT Access Token存储在内存中(例如在JavaScript中作为变量),并在HTTP-唯一Cookie中刷新Token。 他们说: 但是,通过通过刷新令牌间接地保持会话,我们防止了JWT令牌可能存在的直接CSRF

  • 我使用的是Spring Security 3.2.0。带有Java配置的RC2。我设置了一个简单的HttpSecurity配置,要求在/v1/**上进行基本身份验证。GET请求正常,但POST请求失败: 我的安全配置如下所示: 对此有任何帮助,非常感谢。

  • 我正在AngularJS SPA中使用资源所有者密码凭证OAuth 2.0流。有几篇文章(这里,这里…)这个问题的答案解释了我们不应该将刷新令牌存储在(web)客户端(LocalStorage)上,而是将它们加密存储在HttpOnly Cookie中,并使用代理API实现对refreh令牌的解密,从而将其转发给安全令牌服务。 大多数文章都暗示我们应该使用一种常见的保护机制来关注CSRF。我想知道单

  • 我读过关于CSRF和不可预测的同步令牌模式是如何用来防止它的。我不太明白它是如何工作的。 让我们以这个场景为例: 用户使用以下表单登录到站点: 服务器还将令牌存储在会话中。发送请求时,它将表单数据中的令牌与会话中的令牌进行比较。 当黑客可以编写JavaScript代码时,如何防止CSRF: 发送GET请求到站点 接收包含请求表单的html文本。 在html文本中搜索CSRF令牌。 使用该令牌发出恶