【问题标题】:How is it possible for a legitimate user to submit an invalid CSRF token in Rails?合法用户如何在 Rails 中提交无效的 CSRF 令牌?
【发布时间】:2017-05-19 20:24:29
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: ruby-on-rails csrf


    【解决方案1】:

    用户本可以注销并重新登录,但在旧会话中打开了一个带有表单的选项卡。这将发送旧令牌。

    【讨论】:

    • 您好,感谢您的回答。有没有其他方法可以发生这种情况?
    【解决方案2】:

    这是我们所知道的:

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

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

    请注意,这些特别适用于 Rails 4.2。据我了解,Rails 5 中的“执行 CSRF 令牌”功能可能会缓解这些问题。

    1。另一个选项卡中的会话更改

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

    2。缓存

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

    提交缓存的表单会导致 CSRF 错误。随着漏洞的揭幕战结束,Safari 似乎缓存了页面,但删除了会话 cookie。因此不匹配。

    解决此问题的方法是使用指令no-store 设置Cache-Control 标头。 (no-cacheno-store 之间的区别解释了here)。

    欢迎提供更多示例:)。

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 感谢您的回答!两个都不行,抱歉。
      • 这正是我的问题所在:渲染多个表单,尤其是使用render_async,让这很容易被绊倒
      【解决方案4】:

      您写道:鉴于 Rails 会话 cookie 仅在浏览会话结束时(即 Web 浏览器关闭时)过期,有哪些方法可以在不关闭的情况下更改该 cookie(及其包含的 CSRF 令牌)浏览器?


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

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

      一个:当网络浏览会话结束时,存储的 cookie 不会被删除,除非以这种方式编码; cookie 很可能会一直持续到他 cookie 超时,所以很有可能下次访问该页面时会使用旧的令牌,但是由于开发人员一般允许“新登录”刷新页面,因此他们很可能也会在那时。请参阅 @Shikhar-Mann 回复以更好地了解 sign_out_user。

      二:这个问题不需要更改 cookie,问题在于 CSRF 令牌的不匹配。

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

      另外,我可以建议不要保留 CSRF 吗?如果您可以访问表单数据,那么这样做真的没有价值;通常我用 CSRF 数据创建一个隐藏字段,然后用它来回发。 CSRF 不会存活很长时间,并且会话数据是持久的。

      【讨论】:

      • 感谢您的详细解答。由于时间流逝,CSRF 令牌似乎不可能过期,因为它被配置为仅在会话结束时过期。
      • CSRF 并不是这样工作的。它会在一定的时间后过期,因为服务器会在服务器端会话结束时过期。
      • 你可能是对的!但是,在 Rails 中,会话存储在 cookie 中,而不是服务器上。
      • 验证 cookie 中没有超时。如果是这样,那可能是你痛苦的根源。正如您所吸引的那样,Rails 会话通常不会超时(!),而且看起来 Rails 4 已确认不会超时。
      • 好主意。可悲的是,它不会超时!
      【解决方案5】:

      如果关闭浏览器不是一个选项。然后你必须注销用户。为了实现这一点,请将以下代码放在 ApplicationController 中。

      rescue_from ActionController::InvalidAuthenticityToken do |exception|

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

      结束

      P.S:以上来自http://guides.rubyonrails.org/security.html#cross-site-request-forgery-csrf,更多信息请参考。

      【讨论】:

      • 谢谢。有问题的路径仅适用于经过身份验证的用户,因此我认为他们在 CSRF 异常之前没有注销。
      • 是的。因此,当 CSRF 因未知原因而更改时。他们必须被注销。你能排除这种情况下用户可能故意编辑 CSRF 的可能性吗?
      • 不能绝对肯定,但可能性很小。所以在您看来,没有其他方法可以使令牌失效?
      猜你喜欢
      • 2021-03-20
      • 2021-07-23
      • 2020-07-06
      • 1970-01-01
      • 2020-01-02
      • 2014-06-20
      • 1970-01-01
      • 2017-07-15
      • 2016-02-28
      相关资源
      最近更新 更多