Ruby-on-rails 不禁用 CSRF 保护的 Rails API 设计
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7600347/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Rails API design without disabling CSRF protection
提问by Steve Madsen
Back in February 2011, Rails was changed to require the CSRF token for allnon-GETrequests, even those for an API endpoint. I understand the explanation for why this is an important change for browser requests, but that blog post does not offer any advice for how an API should handle the change.
早在 2011 年 2 月,Rails就更改为所有非 GET请求都需要 CSRF 令牌,甚至是 API 端点的请求。我理解为什么这是浏览器请求的重要更改的解释,但该博客文章并未提供有关 API 应如何处理更改的任何建议。
I am not interested in disabling CSRF protection for certain actions.
我对禁用某些操作的 CSRF 保护不感兴趣。
How are APIs supposed to deal with this change? Is the expectation that an API client makes a GET request to the API to get a CSRF token, then includes that token in every request during that session?
API 应该如何应对这种变化?是否期望 API 客户端向 API 发出 GET 请求以获取 CSRF 令牌,然后在该会话期间的每个请求中包含该令牌?
It appears that the token does not change from one POST to another. Is it safe to assume that the token will not change for the duration of the session?
似乎令牌不会从一个 POST 更改为另一个。假设令牌在会话期间不会改变是否安全?
I don't relish the extra error handling when the session expires, but I suppose it is better than having to GET a token before every POST/PUT/DELETE request.
我不喜欢会话过期时的额外错误处理,但我认为这比在每个 POST/PUT/DELETE 请求之前必须获取令牌要好。
回答by Chris Nicola
Old question but security is important enough that I feel it deserves a complete answer. As discussed in this questionthere are still some risk of CSRF even with APIs. Yes browsers are supposed to guard against this by default, but as you don't have complete control of the browser and plugins the user has installed, it's should still be considered a best practice to protect against CSRF in your API.
老问题,但安全性足够重要,我觉得它值得一个完整的答案。正如在这个问题中所讨论的,即使使用 API,仍然存在一些 CSRF 风险。是的,默认情况下浏览器应该防止这种情况发生,但是由于您无法完全控制用户安装的浏览器和插件,因此在您的 API 中防止 CSRF 仍应被视为最佳实践。
The way I've seen it done sometimes is to parse the CSRF meta tag from the HTML page itself. I don't really like this though as it doesn't fit well with the way a lot of single page + API apps work today and I feel the CSRF token should be sent in every request regardless of whether it's HTML, JSON or XML.
我有时看到它完成的方式是从 HTML 页面本身解析 CSRF 元标记。我不太喜欢这个,因为它不适合当今许多单页 + API 应用程序的工作方式,我觉得应该在每个请求中发送 CSRF 令牌,无论它是 HTML、JSON 还是 XML。
So I'd suggest instead passing a CSRF token as a cookie or header value via an after filter for all requests. The API can simply re-submit that back as a header value of X-CSRF-Tokenwhich Rails already checks.
因此,我建议改为通过所有请求的后过滤器将 CSRF 令牌作为 cookie 或标头值传递。API 可以简单地将其作为X-CSRF-TokenRails 已经检查过的标头值重新提交回来。
This is how I did it with AngularJS:
这就是我用 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 cookienamed XSRF-TOKENbut feel free to name it anything you want for your purposes. Then when you submit a POST/PUT/DELETE you should to set the header property X-CSRF-Tokenwhich Rails automatically looks for.
AngularJS 会自动查找名为cookie 的 cookie,XSRF-TOKEN但您可以随意将其命名为您想要的任何名称。然后,当您提交 POST/PUT/DELETE 时,您应该设置X-CSRF-TokenRails 自动查找的 header 属性。
Unfortunately, AngualrJS already sends back the XSRF-TOKENcookie in a header value of X-XSRF-TOKEN. It's easy to override Rails' default behaviour to accomodate this in ApplicationControllerlike this:
不幸的是,AngualrJS 已经将XSRF-TOKENcookie 以X-XSRF-TOKEN. 很容易覆盖 Rails 的默认行为以适应这种ApplicationController情况:
protected
def verified_request?
super || form_authenticity_token == request.headers['X-XSRF-TOKEN']
end
For Rails 4.2 there is a built in helper now for validating CSRF that should be used.
对于 Rails 4.2,现在有一个内置的帮助器来验证应该使用的 CSRF。
protected
def verified_request?
super || valid_authenticity_token?(session, request.headers['X-XSRF-TOKEN'])
end
I hope that's helpful.
我希望这会有所帮助。
EDIT: In a discussion on this for a Rails pull-requestI submitted it came out that passing the CSRF token through the API for login is a particularly bad practice (e.g., someone could create third-party login for your site that uses user credentials instead of tokens). So cavet emptor. It's up to you to decide how concerned you are about that for your application. In this case you could still use the above approach but only send back the CSRF cookie to a browser that already has an authenticated session and not for every request. This will prevent submitting a valid login without using the CSRF meta tag.
编辑:在我提交的关于 Rails 拉取请求的讨论中,通过 API 传递 CSRF 令牌进行登录是一种特别糟糕的做法(例如,有人可以为您的站点创建使用用户凭据的第三方登录而不是令牌)。所以洞穴清空者。由您决定您对应用程序的关注程度。在这种情况下,您仍然可以使用上述方法,但仅将 CSRF cookie 发送回已经具有经过身份验证的会话的浏览器,而不是针对每个请求。这将阻止在不使用 CSRF 元标记的情况下提交有效登录。
回答by Ariejan
Rails works with the 'secure by default' convention. Cross-Site or Cross-Session Request Forgery requires a user to have a browser and another trusted website. This is not relevant for APIs, since they don't run in the browser and don't maintain any session. Therefore, you should disable CSRF for APIs.
Rails 使用“默认安全”约定。跨站点或跨会话请求伪造要求用户拥有浏览器和另一个受信任的网站。这与 API 无关,因为它们不在浏览器中运行,也不维护任何会话。因此,您应该为 API 禁用 CSRF。
Of course, you shouldprotect your API by requiring HTTP Authentication or a custom implemented API token or OAuth solution.
当然,您应该通过要求 HTTP 身份验证或自定义实现的 API 令牌或 OAuth 解决方案来保护您的 API。

