如果空闲,Rails protect_from_forgery会中断登录表单

在我们的Rails应用程序中,我们使用protect_from_forgery来防止CSRF。

然而,我们发现,如果用户访问登录页面,然后在应用程序会话到期时间(比如说15分钟)的时间内去喝杯茶。 登录只是重定向回登录页面,无论是否成功凭据(根据环境,我们得到错误InvalidAuthenticityToken 。)尝试再次登录工作正常。 仅当用户在页面上的时间超过会话时间时才会失败。

我们认为这很奇怪,因为我们尚未登录…所以会话即将到期? 并且肯定会在登录时创建一个新会话,即使已创建并已过期。 事实certificate(阅读本文后: https : //nvisium.com/blog/2014/09/10/understanding-protectfromforgery/ )Rails中的CSRF保护实际上使用会话来检查authenticity_token是否有效。 因此,基本上会话在会话到期时到期(取决于session_store设置),并且如果不再刷新页面,则无法登录。

我们解决了这个问题: skip_before_action :verify_authenticity_token, only: [:create]在我们的SessionsController skip_before_action :verify_authenticity_token, only: [:create] ,但现在这意味着我们的登录表单不再受到保护。

有什么其他选择来解决这个问题? 或者我们使用的解决方案并不像我们想象的那样不安全? 谷歌搜索显示这行代码使用了很多次,但肯定是一个坏主意?

我们的另一个解决方案是允许Exception发生,但是优雅地处理它:

 rescue_from ActionController::InvalidAuthenticityToken do @exception = exception.message render 'errors/500', :status => 500, :layout => 'other' end 

虽然仍然讨厌用户坐在登录页面上的时间超过会话超时(在这种情况下为15分钟)会导致错误!


我们提出的另一个解决方案是将session_store设置为永久,然后手动过期登录会话,如下所示:

 before_action :session_timeout, if: :current_user def session_timeout session[:last_seen_at] ||= Time.now if session[:last_seen_at] < 15.minutes.ago reset_session else session[:last_seen_at] = Time.now end end 

什么会话即将到期?

有问题的会话是Rails会话(参见Ruby on Rails安全指南中的什么是会话? ),这是一个轻量级的数据存储抽象,它在HTTP请求中保持任意状态(默认情况下,在加密cookie中,其他会话存储)也可以配置实现)。 会话状态可以包括经过身份validation的用户ID,但也可以用于其他目的。

在这种情况下,会话不会用于用户身份validation,而是存储临时“真实性令牌”作为Rails安全function的一部分,以保护您的网站免受跨 POST(或其他非)的跨站点请求伪造 (CSRF)攻击-GET)请求。

Rails API文档更详细地描述了此function:

通过在应用程序的呈现HTML中包含令牌,可以保护控制器操作免受跨站点请求伪造(CSRF)攻击。 此令牌作为随机字符串存储在会话中,攻击者无权访问该会话。 当请求到达您的应用程序时,Rails会在会话中使用令牌validation收到的令牌。

另请参阅Ruby on Rails安全指南中的跨站点请求伪造部分,以更全面地了解CSRF攻击是什么,以及基于会话的真实性 – 令牌validation如何防御它。

有什么其他选择来解决这个问题?

  • 增加Rails会话到期时间的持续时间(例如,通过增加传递给cookie_store初始化程序的expire_after选项的持续时间,或完全删除选项以使会话永不过期)。

  • 使用Devise的:timeoutable模块,而不是使用会话cookie过期来使登录的会话到期。

     devise :timeoutable, timeout_in: 15.minutes 

或者我们使用的解决方案并不像我们想象的那样不安全?

配置Rails以跳过verify_authenticity_token回调会禁用该特定控制器操作的CSRF保护,这会使该操作容易受到CSRF攻击。

那么为了重新解释你的问题,是否SessionsController#create禁用CSRF保护#create动作在任何重要/有意义的意义上仍然不安全 ? 虽然这取决于你的申请,但总的来说是的 ,我相信。

考虑这种情况:

针对SessionsController#create #creid的CSRF攻击将允许攻击者恶意指示受害者的浏览器登录攻击者控制下的用户帐户。 下次受害者访问您的网站时(例如,当被攻击者重定向时),他们的浏览器仍然可以登录到攻击者控制的帐户。 然后,受害者可能会在不知不觉中提交敏感的个人数据或在您的网站上执行敏感操作,攻击者可以阅读这些操作。

虽然这种情况可能不如在其他更敏感/破坏性的控制器操作上禁用CSRF保护时可能发生的情况明显更危险,但它仍然暴露出足够的潜在用户/数据隐私问题,我认为它不够安全,不足以推荐禁用默认保护。

 InvalidAuthenticityToken 

当渲染表单(登录表单)已经过期时创建“真实性令牌”时会出现此错误。

我们只有两种方法可以解决此问题。

  • 配置Rails跳过controller#action的verify_authenticity_token – 如问题中已提到的那样不安全
  • (仅限左侧)每当遇到此类exception时,自动重新加载屏幕(此处为“登录”屏幕)。

找到相同的修改后的代码,并在application_controller.rb中添加它以概括整个应用程序的解决方案。

 rescue_from ActionController::InvalidAuthenticityToken do redirect_to root_url end 

另一个选择是在页面上包含一些JS,它会在令牌到期之前自动重新加载一段时间(因此保持身份validation令牌有效)。

如果用户刚刚填写表单,您可能需要检查以在重新加载之前检查鼠标/键盘操作。

似乎这个选项的最大缺点是JS与auth令牌有效的实际时间段脱钩了……所以如果你经常更改你的超时持续时间,它可能会被certificate是一个维护问题。