【问题标题】:rails session being reset intermittentlyRails 会话间歇性重置
【发布时间】:2014-06-26 21:56:45
【问题描述】:

我知道这个话题已经讨论了很多,但我相信我已经找到了它的一个新变体:我有一个 Rails 4 应用程序,它是从 Rails 3 升级而来的,它有 rails_ujscsrf_meta_tags 设置正确。

一旦root_url 被加载到浏览器中,就会有一个javascript 触发一个GET 和一个PUT,每一个都指向应用程序中各自的控制器API。当这 2 个 API 调用被触发时,会话应该有 _csrf_token 那里。这是真的,大多数时候。继续阅读。

问题在于,有时,并非总是如此,我们会看到 PUT 请求发生了一些 InvalidAuthenticityToken 异常。 (是的,我在我们的 API 基础控制器上使用 protect_from_forgery :with => :exception)。

分析来自exception_notification 的转储,我可以看到CSRF_TOKEN 在请求标头中设置正确,但最有趣的是会话上只有session_id。其他所有内容都消失了,包括 _csrf_token

请记住:这是间歇性发生的!所以我相信它一定是某种竞争条件。

此应用托管在 Heroku 上并在 Unicorn 上运行。我无法在本地环境中重现该问题。我还在 github 上阅读了很多 Rails 代码,试图了解它重置会话的流程,但我找不到答案,因为所有 CSRF 保护都设置正确并且问题间歇性发生。

还值得一提的是,我们还没有设置config.secret_key_base。但由于这个问题是间歇性发生的,我不认为这可能是它的根本原因。

另外,我认为值得一提的是我们有两个控制器层次结构:

(1) 所有“正常”应用程序请求都通过继承自 ApplicationController 的控制器

(2)所有API请求都经过控制器,控制器继承自Api::BaseController,直接继承自ActionController::Base

我相信这种控制器方案对每个人来说都是最常见的......

GET 请求的 API 端点正在呈现 json 响应。 PUT 请求的 API 端点正在返回 head :ok

嗯,如果有 Ruby on Rails 专家可以提供帮助,我会很高兴的。

【问题讨论】:

  • 你有没有得到任何地方?
  • 还没有...我仍然面临这个问题。你见过类似的东西吗?
  • 我已经坚持了好几天了 - 设法将其缩小到根本没有设置会话的事实......我已经尝试了很多东西来修复原始问题,这将需要一段时间来回过头来。
  • 我做了很多研究,但我找不到任何解决方案......不过,我认为我不是唯一面临这个问题的人。怎么没有人看到这种情况发生?我相信它与 Heroku 上不同 dynos 提供的请求有关,但我不知道如何在本地重现。

标签: session heroku ruby-on-rails-4


【解决方案1】:

您是否使用标准的基于 cookie 的会话存储?我上次查看的基于 cookie 的会话存储肯定会受到竞争条件的影响,尤其是围绕 AJAX 请求——而竞争条件是基于 cookie 的会话存储设计的固有部分,没有真正的解决方法。

This post from 2011 描述了一个 cookie 存储竞争条件,它也涉及到真实性令牌,可能与您的类似。他们的解决方案是关闭 CSRF 保护,无论如何,对于某些操作,这对我来说听起来不是一个很好的解决方案。

This post from 2014 概述了 cookie 会话存储的竞争条件,并建议使用 ActiveRecord 或其他服务器端存储作为解决方案。 (在我写这篇文章时,那个 URL 是 404'ing,但在谷歌缓存中可用)。

从示例中可以看出,会话 cookie 在每次请求时都会更新,无论会话是否被修改。根据响应最后返回客户端的时间,这就是将在下一次调用中使用的 cookie。例如,如果在我们之前的示例中,如果 get_current_result 的响应比 get_quiz 慢,那么我们的 cookie 将具有正确的数据,并且下一次调用 update_response 会正常工作!所以有时它会起作用,有时并不完全取决于互联网之神。处理这种类型的竞争条件并不有趣。

这意味着在进行多个 ajax 调用时使用 cookie 存储会话是不安全的。下次检查时,会话中保存的所有信息可能是错误的或不存在的。那么解决办法是什么?

...

更好的解决方案是使用服务器端会话存储,如活动记录或内存缓存。这样做可以防止会话数据依赖于客户端 cookie。会话数据不再需要在客户端和服务器之间传递,这意味着当同时生成两个 ajax 时不再有潜在的竞争条件!

我不能确定您是否遇到了这个问题,但值得尝试切换到 ActiveRecord cookie 存储,看看您的问题是否消失。

即使基于 activerecord 的会话存储在 times in the past been subject to race conditions,我也不确定当前的实现是否可行,但至少可以想象它们是可以解决的,而基于 cookie 的存储中的竞争条件是基本的。

ActiveRecord 存储实际上可能会受到 cookie 存储的类似竞争条件的影响——如果您有多个应用进程正在运行(或多线程应用服务器),那么并发请求处理仍然是可能的,非常相似竞争条件可能仍然是可能的。虽然它可能比 cookie 存储的竞争条件更罕见,并且它在理论上是可解决的,尽管可能具有一些特定于域的逻辑,这与 cookie 存储的竞争条件几乎不同如果您正在执行任何异步 ajax,则无法解决。

【讨论】:

    【解决方案2】:

    为了更好的格式,写这个作为答案,也许它应该是一个评论。

    我遇到了类似的问题,根本原因是我的应用名为current_user 之前 protect_from_forgery执行,这是current_user在Devise中的实现:

          def current_#{mapping}
            @current_#{mapping} ||= warden.authenticate(scope: :#{mapping})
          end
    

    Devise 有一个功能config.clean_up_csrf_token_on_authentication = true

    所以问题是,在current_user 被调用后,csrf 令牌已被重置。因为它调用warden.authenticate。然后在运行protect_from_forgery 时。引发了一个 csrf 错误。您的会话将被重置或引发异常。

    希望这能有所帮助。

    【讨论】:

      猜你喜欢
      • 2010-12-03
      • 2021-09-09
      • 2010-12-07
      • 1970-01-01
      • 2011-08-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-16
      相关资源
      最近更新 更多