rails会话间歇性地重置

时间:2014-06-26 21:56:45

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

我知道这个话题已经讨论了很多,但我相信我找到了一个新的变体:我有一个Rails 4应用程序,它从Rails 3升级,并且有{{ 1}}和rails_ujs设置正确。

在浏览器中加载csrf_meta_tags后,会出现一个触发GET和PUT的javascript,每个javascript都会触发应用程序中各自的控制器API。到目前为止,这两个API调用被触发,会话应该在那里有root_url。大多数时候都是如此。继续阅读。

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

protect_from_forgery :with => :exception分析转储,我可以看到exception_notification在请求标头中设置正确,但最有趣的是会话上只有CSRF_TOKEN 。其他一切都消失了,包括session_id

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

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

还值得一提的是,我们尚未设置_csrf_token。但由于这个问题是间歇性地发生的,我不认为这可能是它的根本原因。

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

(1)所有'正常'应用程序请求通过继承自config.secret_key_base

的控制器

(2)所有API请求都通过继承自Api :: BaseController的控制器,该控制器直接从ApplicationController继承

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

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

好吧,如果Ruby on Rails专家可以为此提供帮助,我很乐意。

2 个答案:

答案 0 :(得分:2)

您使用的是标准的基于cookie的会话存储吗?基于cookie的会话存储,最后我看,绝对受到竞争条件的限制,尤其是围绕AJAX请求 - 并且竞争条件是基于cookie的会话存储设计的固有部分,没有真正的修复它的方法。

This post from 2011描述了一个cookie商店竞争条件,它也涉及真实性令牌,可能与您的类似。他们的解决方案是关闭CSRF保护,无论如何都要采取某些行动,这听起来不是一个很好的解决方案。

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

  

从示例中可以看出,无论会话是否被修改,会话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,则无法解决。

答案 1 :(得分:1)

为了获得更好的格式,请将其写为答案,但也许应该是评论。

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

      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错误已经引发。您的会话将被重置或引发异常。

希望这可以提供帮助。