不禁用CSRF保护的情况下设计Rails API

51

早在2011年2月,Rails就进行了修改,要求对于所有非GET请求都需要CSRF令牌验证,即使是针对API端点的请求。我理解为什么这对于浏览器请求来说是一个重要的变化,但是该博客文章没有提供任何有关API如何处理该变化的建议。

我不想禁用某些操作的CSRF保护。

API应该如何处理这个变化?预期API客户端会向API发出GET请求以获取CSRF令牌,然后在该会话的每个请求中包含该令牌吗?

似乎令牌不会从一个POST到另一个POST改变。是否可以安全地假设令牌在整个会话期间不会改变?

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

2个回答

54

虽然这是一个老问题,但安全性很重要,我认为它值得完整的回答。正如在question中讨论的那样,即使是通过API,仍然存在CSRF的某些风险。是的,浏览器默认情况下应该防止此类攻击,但由于您无法完全控制用户安装的浏览器和插件,因此在API中防范CSRF仍应被视为最佳实践。

有时候,我看到的做法是从HTML页面本身解析CSRF meta标签。但我并不喜欢这种方法,因为它与当今许多单页面+API应用程序的工作方式不符,我认为无论是HTML、JSON还是XML,都应该在每个请求中发送CSRF令牌。

因此,我建议通过过滤器将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 自动寻找名为 XSRF-TOKEN 的 cookie,但您可以根据您的需要随意更改名称。然后,在提交 POST/PUT/DELETE 请求时,应设置头部属性 X-CSRF-Token,Rails 会自动查找。

不幸的是,AngualrJS 已经将 XSRF-TOKEN cookie 发送回一个名为 X-XSRF-TOKEN 的头部值中。您可以轻松地在 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

我希望这对你有所帮助。

编辑:在我提交的Rails pull-request的讨论中,发现通过API传递CSRF令牌进行登录是一种特别糟糕的做法(例如,某人可以创建使用用户凭据而不是令牌的第三方登录您的网站)。所以要小心。由你来决定你对应用程序的担忧程度。在这种情况下,您仍然可以使用上述方法,但仅将CSRF cookie发送回已经具有经过身份验证会话的浏览器,而不是每个请求都发送。这将防止在不使用CSRF元标记的情况下提交有效的登录。


4
您的修改至关重要:服务器应仅为经过身份验证的用户设置CSRF cookie。 - brookr
你认为授权方面应该怎么做呢?只是使用普通的基于表单的授权,将CSRF令牌像往常一样设置在HTML中,然后在API中使用基于头部或Cookie的CSRF吗? - Chris Nicola
设置一个没有 httponly 的 cookie 似乎不安全。使用 httponly 是否可以防止 JS 访问 CSRF cookie?如果您使用 fetch 的 credentials: 'same-origin' 参数,则它们将随请求一起传递。为什么要使用非常规的 cookie 名称呢?如果您使用 X-CSRF-Token,ActionDispatch 将自动检查它,无需覆盖 verified_request?。我建议使用:cookies["X-CSRF-Token"] = { value: form_authenticity_token, httponly: true } - Olivier Lacan
HttpOnly不会生效,因为您的JS框架需要访问cookie才能将其作为标头值返回。但是,您可以使用meta标记支持。另外,当我编写此代码时,ActionDispatch没有自动检查X-CSRF-Token。 - Chris Nicola
有人遇到过这样的问题吗?在Rails过滤器中明确将令牌设置为httponly: false,但在浏览器中令牌始终是httponly。我无法让我的React前端能够访问这些cookie。 - Aaron Parisi
显示剩余3条评论

7
Rails遵循“默认安全”的约定。跨站请求伪造(CSRF)需要用户使用浏览器和另一个可信任的网站。但对于API来说这并不相关,因为他们不在浏览器中运行,也不维护任何会话。所以,你应该为API禁用CSRF。
当然,你也应该通过要求HTTP身份验证或自定义实现的API令牌或OAuth解决方案来保护你的API。

2
我链接的页面有不同意见:“某些浏览器插件和HTTP重定向的组合可以用来欺骗用户的浏览器,使其发出跨域请求,其中包括攻击者指定的任意HTTP头。攻击者可以利用这一点来伪造ajax和API请求,并绕过内置的CSRF保护,成功攻击应用程序。” - Steve Madsen
14
有时候浏览器本身就是API的客户端,因此这句话并不适用于API。 - antinome
1
https://dev59.com/9mox5IYBdhLWcg3wNBnS#10049965 - montrealmike

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接