单页应用程序和CSRF令牌

我需要使用单页应用程序(React,Ember,Angular,我不关心)和Rails CSRF保护机制。

我想知道是否需要在ApplicationController创建令牌evey时间,如下所示:

 class ApplicationController < ActionController::Base after_action :set_csrf_cookie def set_csrf_cookie cookies["X-CSRF-Token"] = form_authenticity_token end end 

或者我可以只创建一次令牌

每会话或每(非GET)请求?

我认为令牌在会话有效之前仍然有效,对吧?

澄清

每当我浏览页面时,我都会看到Rails默认应用程序(服务器呈现页面)更新csrf-token。 所以每次都改变。

因此,在我的情况下,如果我为每个after_action创建一个新令牌,那么之前的CSRF令牌仍然适合该会话。 那么,如何使前一个令牌无效? 我必须?

因为只有我无效才有意义,对吧?

如果你使用SPA应用程序,那么你大多只使用你的Rails作为API。 CSRF令牌专为服务器呈现而设计…而不是SPA。 在SPA中,您已在身份validation期间使用令牌,因此无需为CSRF使用其他令牌。 CSRF被设计为对跨站点调用的保护,但API本身的设计允许来自任何地方的请求,直到它们被认证。

只需为您的API禁用它,就是这样。 我会使用一些API命名空间并设置一个BaseController ,它将inheritance所有API控制器。 你应该设置protect_from_forgery

 class API::BaseController < ApplicationController protect_from_forgery with: :null_session end 

客户端(SPA)

您只需要在每个会话中获取一次 CSRF令牌。 您可以在浏览器中保留它并在每个(非GET)请求上发送它。

Rails似乎会在每个请求上生成一个新的CSRF令牌,但它将接受来自该会话的任何生成的令牌。 实际上,它只是为每个请求使用一次性填充屏蔽单个令牌,以防止SSL BREACH攻击。 有关详细信息, 请访问https://stackoverflow.com/a/49783739/2016618 。 您无需跟踪/存储这些令牌。

服务器端

我强烈建议使用Rails的protect_from_forgery指令,而不是自己在头文件中编码CSRF令牌。 它将为每个请求生成不同的屏蔽标记。

你可以用很多代码自己重现这个,但我不明白为什么你需要。

您是否需要使用API​​进行CSRF保护?

是! 如果您使用cookie进行身份validation,则需要CSRF保护。 这是因为cookie随每个请求一起发送,因此恶意网站可以向您的站点发送POST请求并代表登录用户执行请求。 CSRF令牌可以防止这种情况,因为恶意站点不会知道CSRF令牌。

我不知道你面临的确切问题。 但是,如果您在New Rails版本中遇到CSRF问题并且需要在ajax请求中包含Rails CSRF令牌,则可以按照以下步骤操作。

最近我使用了Rails 5.1应用程序。

当使用ajax调用从API获取一些数据时,我遇到了CSRF令牌问题:

 'WARNING: Can't verify CSRF token authenticity rails' 

原因是

Rails 5.1默认删除了对jqueryjquery_ujs的支持,并添加了

 //= require rails-ujs in application.js 

它做了以下事情:

  1. 强制确认各种动作的对话框;
  2. 从超链接发出非GET请求;
  3. 使表单或超链接与Ajax异步提交数据;
  4. 提交按钮会在表单提交时自动禁用,以防止双击。 (来自: https : //github.com/rails/rails-ujs/tree/master )

但默认情况下,它不包括ajax请求的csrf标记。 要小心。 我们必须明确地传递它:

 $( document ).ready(function() { $.ajaxSetup({ headers: { 'X-CSRF-Token': Rails.csrfToken() } }); ---- ---- }); 

请注意,在Rails 5.1版本中,您可以在js中获得“Rails”类,并且可以使用这些函数。

更新:如果您正在使用Rails服务器端和其他前端,您真的不想使用Rails提供的CSRF令牌。 因为您正在使用哪种后端服务并不重要。

如果你的目标是阻止CSRF,你需要在后端设置CORS,那就是你的Rails后端。 Rails现在在初始化程序中为此提供单独的文件。 您可以提及允许哪些站点向您的后端发送ajax请求。

在这里编辑:

 config/initializers/cors.rb 

如果您需要身份validation,请使用基本身份validation,令牌身份validation或JWT

我只想回答这个问题。 本文将介绍完整的详细信息: https : //blog.eq8.eu/article/rails-api-authentication-with-spa-csrf-tokens.html

鉴于Rails正在为SPA设置cookie

CSRF令牌在会话期间有效。 因此,只需在登录后的会话期间生成一个CSRF令牌即可。

 class LoginController < ApplicationController def create if user_password_match # .... cookie[:my_csrf_token]= form_authenticity_token end end end 

或者您可以像提议的那样刷新cookie

 class ApplicationController < ActionController::Base after_action :set_csrf_cookie def set_csrf_cookie cookies["my_csrf_token"] = form_authenticity_token end end 

您的SPA只需要读取cookie并将其设置为标题。

两者都同样有效

或者您可以只提供CSRF令牌作为登录响应,SPA会将其存储在某处并在X-CSRF-Token标头中使用它:

 curl POST https://api.my-app.com/login.json -d "{"email":'equivalent@eq8.eu", "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" 

鉴于Rails正在接受仅标头身份validation

如果您没有使用cookie来发送session_id,那么您的API仅使用Authentication标头进行身份validation。 那你就不需要CSRF保护了。

没有会话cookie没有CSRF问题!

例:

 curl POST https://api.my-app.com/login.json -d "{"email":'equivalent@eq8.eu", "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" 

再次,这只是当您不使用cookie进行用户识别时! 因此,CSRF在这种情况下不是问题,但您仍然可以防止跨站点脚本攻击,确保您的通信仅限HTTP,等等......