单页应用程序和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默认删除了对jquery和jquery_ujs的支持,并添加了
//= require rails-ujs in application.js
它做了以下事情:
- 强制确认各种动作的对话框;
- 从超链接发出非GET请求;
- 使表单或超链接与Ajax异步提交数据;
- 提交按钮会在表单提交时自动禁用,以防止双击。 (来自: 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,等等......