不禁用 CSRF 保护的 Rails API 设计

Posted

技术标签:

【中文标题】不禁用 CSRF 保护的 Rails API 设计【英文标题】:Rails API design without disabling CSRF protection 【发布时间】:2011-11-27 20:36:18 【问题描述】:

早在 2011 年 2 月,Rails 已更改为 require the CSRF token for all non-GET 请求,即使是那些用于 API 端点的请求。我理解为什么这是对浏览器请求的重要更改的解释,但该博客文章没有就 API 应如何处理更改提供任何建议。

我对为某些操作禁用 CSRF 保护不感兴趣。

API 应该如何应对这种变化?是否期望 API 客户端向 API 发出 GET 请求以获取 CSRF 令牌,然后在该会话期间将该令牌包含在每个请求中?

似乎令牌不会从一个 POST 更改为另一个。假设令牌在会话期间不会更改是否安全?

我不喜欢会话过期时的额外错误处理,但我认为这比在每次 POST/PUT/DELETE 请求之前都必须获取令牌要好。

【问题讨论】:

【参考方案1】:

老问题,但安全性非常重要,我觉得它值得一个完整的答案。正如question 中所讨论的,即使使用 API,仍然存在一些 CSRF 风险。是的,浏览器应该默认防止这种情况发生,但由于您无法完全控制用户安装的浏览器和插件,因此仍应将其视为防止 API 中的 CSRF 的最佳做法。

有时我看到的做法是从 html 页面本身解析 CSRF 元标记。我不太喜欢这个,因为它不适合当今许多单页 + API 应用程序的工作方式,而且我觉得 CSRF 令牌应该在每个请求中发送,无论它是 HTML、JSON 还是 XML。

所以我建议改为通过所有请求的后过滤器将 CSRF 令牌作为 cookie 或标头值传递。 API 可以简单地将其作为 X-CSRF-Token 的标头值重新提交回来,Rails 已经检查了该值。

这就是我使用 AngularJS 的方式:

  # In my ApplicationController
  after_filter :set_csrf_cookie

  def set_csrf_cookie
    if protect_against_forgery?
      cookies['XSRF-TOKEN'] = form_authenticity_token
    end
  end

AngularJS automatically looks for a cookie 命名为 XSRF-TOKEN,但您可以根据自己的目的随意命名。然后,当您提交 POST/PUT/DELETE 时,您应该设置 Rails 自动查找的标题属性 X-CSRF-Token

不幸的是,AngualrJS 已经在X-XSRF-TOKEN 的标头值中发回了XSRF-TOKEN cookie。在ApplicationController 中覆盖 Rails 的默认行为很容易,如下所示:

  protected

  def verified_request?
    super || form_authenticity_token == request.headers['X-XSRF-TOKEN']
  end

对于 Rails 4.2,现在有一个内置的帮助器来验证应该使用的 CSRF。

  protected

  def verified_request?
    super || valid_authenticity_token?(session, request.headers['X-XSRF-TOKEN'])
  end

我希望这会有所帮助。

编辑:在discussion on this for a Rails pull-request 我提交的结果表明,通过 API 传递 CSRF 令牌进行登录是一种特别糟糕的做法(例如,有人可以为您的网站创建使用用户凭据而不是令牌的第三方登录)。所以自告奋勇。由您决定您对应用程序的关注程度。在这种情况下,您仍然可以使用上述方法,但仅将 CSRF cookie 发送回已经具有经过身份验证的会话的浏览器,而不是针对每个请求。这将阻止在不使用 CSRF 元标记的情况下提交有效登录。

【讨论】:

您的编辑至关重要:服务器应该只为经过身份验证的用户设置 CSRF cookie。 那么你对 auth 有什么建议呢?只是像往常一样在 HTML 中设置 CSRF 令牌的基于普通表单的身份验证,然后是带有标头或基于 cookie 的 CSRF 的 API? 在没有httponly 的情况下设置 cookie 似乎并不安全。 httponly 不会阻止 JS 访问 CSRF cookie 吗?相反,如果您使用 fetch's credentials : 'same-origin' parameter,它们将与请求一起传递。为什么要为 cookie 使用非常规名称?如果您使用X-CSRF-Token ActionDispatch 将自动检查它而不覆盖verified_request?需要。我推荐:cookies["X-CSRF-Token"] = value: form_authenticity_token, httponly: true HttpOnly 将不起作用,因为您的 JS 框架确实需要访问 cookie 才能将其作为标头值返回。但是,您可以只使用meta tag support。此外,当我编写此 ActionDispatch 时,并没有自动检查 X-CSRF-Token。 @ChrisNicola 今天仍然没有。【参考方案2】:

Rails 使用“默认安全”约定。跨站点或跨会话请求伪造要求用户拥有浏览器和另一个受信任的网站。这与 API 无关,因为它们不在浏览器中运行,也不维护任何会话。因此,您应该为 API 禁用 CSRF。

当然,您应该通过要求 HTTP 身份验证或自定义实施的 API 令牌或 OAuth 解决方案来保护您的 API。

【讨论】:

我链接的页面不同意:“浏览器插件和 HTTP 重定向的某些组合可用于欺骗用户的浏览器发出包含攻击者指定的任意 HTTP 标头的跨域请求。攻击者可以利用它来欺骗 ajax 和 API 请求并绕过内置的 CSRF 保护并成功攻击应用程序。” “这与 API 无关,因为它们不在浏览器中运行”:有时浏览器 API 的客户端——即跨度> ***.com/questions/9362910/…

以上是关于不禁用 CSRF 保护的 Rails API 设计的主要内容,如果未能解决你的问题,请参考以下文章

什么时候可以禁用 csrf 保护

如何确保 Rails API 不受 CSRF 的影响?

在 Rails 中使用 HTTP GET 请求进行 CSRF 保护

在 Rails 3 中关闭 CSRF 令牌

在rails 3中关闭CSRF令牌

Spring-Security对CSRF攻击的支持