合法用户怎么可能在 Rails 中提交无效的 CSRF 令牌?

How is it possible for a legitimate user to submit an invalid CSRF token in Rails?

我们的错误日志偶尔会包含导致 ActionController::InvalidAuthenticityToken 错误的合法表单提交。

我的假设是,存储在用户会话 cookie 中的 CSRF 令牌在表单加载之后但提交之前的某个时间点发生了变化。这会导致 POSTed 令牌与 cookie 中的令牌不匹配,从而导致此错误。

鉴于 Rails 会话 cookie 仅在浏览会话结束时(即当网络浏览器关闭时)过期,此 cookie(及其包含的 CSRF 令牌)可以通过哪些方式在不关闭浏览器的情况下更改?

我们正在使用 cookie 来存储会话数据,这是 Rails 的默认行为。

如果无法关闭浏览器。然后你必须注销用户。要实现这一点,请将以下代码放在 ApplicationController 中。

rescue_from ActionController::InvalidAuthenticityToken 做|异常|

sign_out_user # 将销毁用户 cookie 的示例方法

结束

P.S:以上内容来自http://guides.rubyonrails.org/security.html#cross-site-request-forgery-csrf,因此请参阅以获取更多信息。

用户可能已经注销并重新登录,但有一个选项卡上有一个从旧会话中打开的表单。那将发送旧令牌。

您是否使用 Ajax 表单提交请求?您的页面是否有多种形式?如果是这样,请检查您的代码是否随请求一起提交了正确的 csrf 令牌。我们有类似的问题,页面是用一个 csrf 令牌呈现的,我们使用这个令牌提交第一个表单,我们会得到另一个 csrf 令牌,但第二个发送旧的 csrf 令牌导致错误。 我不确定这有什么用,但想分享我面临的类似问题

您写道:鉴于 Rails 会话 cookie 仅在浏览会话结束时(即当网络浏览器关闭时)过期,此 cookie(及其包含的 CSRF 令牌)的方式是什么) 可以在不关闭浏览器的情况下更改?


首先,你的假设是成立的。不过,您是如何到达那里的可能值得考虑。

您提出的假设需要关注两个层面。

一:当网络浏览会话结束时,存储的 cookie 不会被删除,除非以这种方式进行编码; cookie 可能会持续到 cookie 超时,因此下次访问页面可能会使用旧令牌,但由于开发人员通常允许 "new login" 刷​​新页面,因此他们也可能会刷新令牌那时候。请参阅@Shikhar-Mann 回复以更好地理解 sign_out_user.

二:不必为此问题更改 cookie,问题是 CSRF 令牌不匹配。

所以根本问题应该是:我们可以通过哪些方式获得不匹配的 CSRF 令牌,这将更容易回答:由于长时间等待导致客户端上的旧数据,这会导致服务器超时,在延迟期间使 CSRF 令牌无效。如果网页不configured/created也超时重定向,客户端/用户永远不会知道。

此外,我可以建议不要保留 CSRF 吗?如果您可以访问表单数据,那么这样做确实没有价值;通常我用 CSRF 数据创建一个隐藏字段,然后用它返回 post。 CSRF 不会存在很长时间,会话数据会持续存在。

这是我们所知道的:

  • 导致 InvalidAuthenticityToken 异常的合法表单提交以某种方式丢失了它们的原始 CSRF 令牌。
  • 令牌保存在 session 中,后者保存在加密的 cookie 中。所以这个错误意味着他们的 session cookie 在表单生成后发生了变化。
  • 除非用户的浏览器 window 关闭,否则 session cookie 不会过期。

我现在认为合法用户可以通过两种方式提交无效的 CSRF 令牌。

请注意,这些特别适用于 Rails 4.2。据我了解,Rails 5 中的 "per-form CSRF tokens" 功能可能会缓解它们。

1。 Session 在另一个选项卡中更改

根据 @crazymykl's answer,用户可以打开表单,然后在另一个选项卡中注销。这会导致存储在用户浏览器中的 session cookie 发生变化。当他们返回原始选项卡并提交表单时,表单中的标记与 session 中的标记不匹配,并且会弹出错误。

2。缓存

根据 this rails bug,Safari 在某些情况下的缓存行为很奇怪。告诉它使用与上次相同的 windows 重新打开(通过 Safari > Preferences > General),打开表单并退出 Safari 会导致重新显示表单。

提交该缓存表单会导致 CSRF 错误。正如漏洞的开启者所总结的那样,Safari 似乎缓存了该页面,但丢弃了 session cookie。因此不匹配。

这个问题的解决办法是用指令no-store设置一个Cache-Controlheader。 (no-cacheno-store 之间的差异解释 here)。

欢迎提供更多示例 :)。