合法用户如何在Rails中提交无效的CSRF令牌?

djb*_*djb 8 ruby-on-rails csrf

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

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

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

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

cra*_*ykl 8

用户可能已注销并重新登录,但有一个表单,其中包含从旧会话打开的表单.那将发送旧令牌.


djb*_*djb 6

这是我们所知道的:

  • 导致InvalidAuthenticityToken异常的合法表单提交以某种方式丢失了其原始 CSRF 令牌。
  • 令牌保存在会话中,会话保存在加密的 cookie 中。因此,此错误意味着自表单生成以来,他们的会话 cookie 已发生更改。
  • 除非用户的浏览器窗口关闭,否则会话 cookie 不会过期。

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

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

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

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

2. 缓存

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

提交该缓存表单会导致 CSRF 错误。作为 bug 的开端,Safari 似乎缓存了页面,但删除了会话 cookie。因此出现了不匹配。

Cache-Control此问题的解决方案是使用指令设置标头no-storeno-cache(和之间的区别在这里no-store解释)。

欢迎进一步的例子:)。