javascript 单页应用程序和 CSRF 令牌
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/50159847/
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
Single Page Application and CSRF Token
提问by Sarkom
I need to use a Single Page Application (React, Ember, Angular, I don't care) with Rails CSRF protection mechanism.
我需要使用带有 Rails CSRF 保护机制的单页应用程序(React、Ember、Angular,我不在乎)。
I'm wondering if I need to create a token evey time in the ApplicationControllerlike this:
我想知道我是否需要ApplicationController像这样每次都创建一个令牌:
class ApplicationController < ActionController::Base
after_action :set_csrf_cookie
def set_csrf_cookie
cookies["X-CSRF-Token"] = form_authenticity_token
end
end
or I can just create a token once.
或者我可以只创建一个令牌一次。
Per session or per (non-GET) request?
每个会话还是每个(非 GET)请求?
I think the token is still valid until the session is valid, right?
我认为令牌在会话有效之前仍然有效,对吗?
CLARIFY:
澄清:
I see Rails default application (server-rendered pages) update csrf-token each time I navigate a page. So every time it changes.
每次浏览页面时,我都会看到 Rails 默认应用程序(服务器呈现的页面)更新 csrf-token。所以每次都变了。
So in my situation if I create a new token for each after_actionthe previous CSRF-Token is still good for that session. So, how to invalidate the previous token? I have to?
因此,在我的情况下,如果我为每个after_action先前的 CSRF-Token创建一个新令牌,则对该会话仍然有好处。那么,如何使之前的令牌失效呢?我必须?
Because only if I invalidate it makes sense, right?
因为只有我无效它才有意义,对吗?
回答by Sarkom
Client side (SPA)
客户端(SPA)
You only need to grab the CSRF token once per session. You can hold onto it in the browser and send it on every (non-GET) request.
您只需要在每个会话中获取一次CSRF 令牌。您可以在浏览器中保留它并在每个(非 GET)请求中发送它。
Rails will appear to generate a new CSRF token on every request, but it will accept any generated token from that session. In reality, it is just masking a single token using a one-time pad per request, in order to protect against SSL BREACH attack. More details at https://stackoverflow.com/a/49783739/2016618. You don't need to track/store these tokens.
Rails 似乎会在每个请求上生成一个新的 CSRF 令牌,但它会接受来自该会话的任何生成的令牌。实际上,它只是对每个请求使用一次性填充来屏蔽单个令牌,以防止 SSL BREACH 攻击。更多详细信息,请访问https://stackoverflow.com/a/49783739/2016618。您不需要跟踪/存储这些令牌。
Server side
服务器端
I strongly suggest using Rails's protect_from_forgerydirective rather than encoding the CSRF token in a header yourself. It will generate a different masked token per request.
我强烈建议使用 Rails 的protect_from_forgery指令,而不是自己在标头中编码 CSRF 令牌。它将为每个请求生成不同的掩码令牌。
You can certainly reproduce this yourself with not that much code, but I don't see why you'd need to.
你当然可以不用那么多代码自己重现这个,但我不明白你为什么需要这样做。
Do you need CSRF protection with an API?
您是否需要使用 API 进行 CSRF 保护?
Yes!If you are authenticating with a cookie, you need CSRF protection. This is because cookies are sent with every request, so a malicious website could send a POST request to your site and perform requests on behalf of a logged in user. The CSRF token prevents this, because the malicious site won't know the CSRF token.
是的!如果您使用 cookie 进行身份验证,则需要 CSRF 保护。这是因为 cookie 随每个请求一起发送,因此恶意网站可以向您的站点发送 POST 请求并代表登录用户执行请求。CSRF 令牌阻止了这种情况,因为恶意站点不会知道 CSRF 令牌。
回答by AntonTkachov
If you go with SPA application then you mostly use your Rails only as an API. CSRF token was designed for server rendering... not SPA. In SPA you already use token during authentication, so no need to use another token for CSRF. CSRF was designed as a protection for cross site calls, but API itself designed in a way that it allows request from anywhere until, they are authenticated.
如果您使用 SPA 应用程序,那么您主要将 Rails 用作 API。CSRF 令牌是为服务器渲染而设计的……不是 SPA。在 SPA 中,您已经在身份验证期间使用了令牌,因此无需为 CSRF 使用另一个令牌。CSRF 旨在保护跨站点调用,但 API 本身的设计方式允许来自任何地方的请求,直到它们通过身份验证为止。
Just disable it for your API and that's all. I would go with some APInamespace and setup a BaseController, that will be inherited for all API controllers. There you should set protect_from_forgery:
只需为您的 API 禁用它即可。我会使用一些API命名空间并设置一个BaseController,它将为所有 API 控制器继承。在那里你应该设置protect_from_forgery:
class API::BaseController < ApplicationController
protect_from_forgery with: :null_session
end
回答by Abhi
I don't know what exact issue you are facing. But if you are getting CSRF issues in New Rails versions and need to include Rails CSRF tokens in ajax requests you can follow the steps below.
我不知道您面临的具体问题是什么。但是,如果您在新 Rails 版本中遇到 CSRF 问题并且需要在 ajax 请求中包含 Rails CSRF 令牌,您可以按照以下步骤操作。
Recently I used Rails 5.1 application.
最近我使用了 Rails 5.1 应用程序。
When using ajax calls to fetch some data from APIs I was getting CSRF token issues:
当使用 ajax 调用从 API 获取一些数据时,我遇到了 CSRF 令牌问题:
‘WARNING: Can't verify CSRF token authenticity rails'
The reason was
原因是
Rails 5.1removed support for jqueryand jquery_ujsby default, and added
Rails 5.1默认删除了对jquery和jquery_ujs的支持,并添加了
//= require rails-ujs in application.js
It does the following things:
它执行以下操作:
- force confirmation dialogs for various actions;
- make non-GET requests from hyperlinks;
- make forms or hyperlinks submit data asynchronously with Ajax;
- have submit buttons become automatically disabled on form submit to prevent double-clicking. (from: https://github.com/rails/rails-ujs/tree/master)
- 各种动作的强制确认对话框;
- 从超链接发出非 GET 请求;
- 使表单或超链接与 Ajax 异步提交数据;
- 提交按钮在表单提交时自动禁用以防止双击。(来自:https: //github.com/rails/rails-ujs/tree/master)
But it is not including the csrf token for ajax request by default. Beware of that. We have to explicitly pass it like:
但默认情况下它不包括 ajax 请求的 csrf 令牌。小心那个。我们必须像这样明确地传递它:
$( document ).ready(function() {
$.ajaxSetup({
headers: {
'X-CSRF-Token': Rails.csrfToken()
}
});
----
----
});
Note that in Rails 5.1 version, you get ‘Rails' class in js, and can make use of the functions.
请注意,在 Rails 5.1 版本中,您可以在 js 中获得“Rails”类,并且可以使用这些功能。
Update:If you are using Rails server side and other front end, you really don't want to use Rails provided CSRF tokens. Because Its really not matter which backend service you are using.
更新:如果您使用 Rails 服务器端和其他前端,您真的不想使用 Rails 提供的 CSRF 令牌。因为您使用的后端服务真的无关紧要。
If your aim is to block CSRF, you need to set up CORS in backend, that is your Rails backend. Rails is now providing a separate file in initializer for this. You can mention which sites are allowed to send ajax requests to your backend.
如果您的目标是阻止 CSRF,则需要在后端设置 CORS,即您的 Rails 后端。Rails 现在为此在初始化程序中提供了一个单独的文件。您可以提及允许哪些站点向您的后端发送 ajax 请求。
Edit here:
在这里编辑:
config/initializers/cors.rb
If you want authentication, use basic auth, token auth Or JWT
如果需要认证,使用基本认证、令牌认证或 JWT
回答by equivalent8
I'll just answer the question. Full details are explained in this article: https://blog.eq8.eu/article/rails-api-authentication-with-spa-csrf-tokens.html
我来回答一下问题。本文解释了全部细节:https: //blog.eq8.eu/article/rails-api-authentication-with-spa-csrf-tokens.html
Given Rails is setting cookie for SPA
鉴于 Rails 正在为 SPA 设置 cookie
CSRF tokens are valid during the lifetime of the session. Therefore yes it's ok just to generate one CSRF token for the duration of session after login.
CSRF 令牌在会话的生命周期内有效。因此,是的,在登录后的会话期间生成一个 CSRF 令牌是可以的。
class LoginController < ApplicationController
def create
if user_password_match
# ....
cookie[:my_csrf_token]= form_authenticity_token
end
end
end
or you can refresh the cookie the same way as you are proposing
或者您可以按照您提议的方式刷新 cookie
class ApplicationController < ActionController::Base
after_action :set_csrf_cookie
def set_csrf_cookie
cookies["my_csrf_token"] = form_authenticity_token
end
end
Your SPA just needs to read the cookie and set it as header.
您的 SPA 只需要读取 cookie 并将其设置为标题。
Both are equally valid
两者同样有效
or You can just provide the CSRF token as a login response and SPA will store it somewhere and use it in the X-CSRF-Tokenheader:
或者您可以只提供 CSRF 令牌作为登录响应,SPA 会将其存储在某处并在X-CSRF-Token标头中使用它:
curl POST https://api.my-app.com/login.json -d "{"email":'[email protected]", "password":"Hello"}" -H 'ContentType: application/json'
# Cookie with session_id was set
# response:
{ "login": "ok", "csrf": 'yyyyyyyyy" }
# next request
curl POST https://api.my-app.com/transfer_my_money -d "{"to_user_id:":"1234"}" -H "ContentType: application/json" -H "X-CSRF-Token: yyyyyyyyy"
Given Rails is accepting header only authentication
鉴于 Rails 接受仅标头身份验证
if you are not using cookie to send session_id, therefore your API is using just Authenticationheader for authentication. Then you don't need CSRF protection.
如果您不使用 cookie 发送 session_id,那么您的 API 仅使用Authentication标头进行身份验证。那么你就不需要 CSRF 保护了。
No session cookie no CSRF problems !
没有会话 cookie 没有 CSRF 问题!
Example:
例子:
curl POST https://api.my-app.com/login.json -d "{"email":'[email protected]", "password":"Hello"}" -H 'ContentType: application/json'
# No cookie/session is set
# response:
{ "login": "ok", "jwt": "xxxxxxxxxxx.xxxxxxxx.xxxxx" }
# Next Request:
curl POST https://api.my-app.com/transfer_my_money -d "{"to_user_id:":"1234"}" -H "ContentType: application/json" -H "Authentication: Bearer xxxxxxxxxxx.xxxxxxxx.xxxxx"
Once again, this is only when you don't use cookies for user identification ! So CSRF is not an issue in this case but you still protect from Cross site scripting attack, make sure your communication is HTTPs only, etc...
再一次,这只是当您不使用 cookie 进行用户识别时!因此,在这种情况下,CSRF 不是问题,但您仍然可以防止跨站点脚本攻击,确保您的通信仅是 HTTPs,等等......

