单页应用程序和 CSRF 令牌

IT技术 javascript ruby-on-rails reactjs session csrf
2021-03-12 05:30:10

我需要使用带有 Rails CSRF 保护机制的单页应用程序(React、Ember、Angular,我不在乎)。

我想知道我是否需要ApplicationController像这样每次都创建一个令牌

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-Token创建一个新令牌,则对该会话仍然有好处。那么,如何使之前的令牌失效呢?我必须?

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

4个回答

客户端(SPA)

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

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

服务器端

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

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

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

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

恶意网站还可以向网络服务器发送请求以获取 CSRF 令牌,然后发布任何内容。
2021-04-28 05:30:10
只有在您的端点接受application/x-www-form-urlencoded正确的情况下才能访问来自 POST 请求的这种攻击我想对于许多 RESTful API 来说,它们只允许带有application/json.
2021-05-01 05:30:10
@BasheerKharoti 同源策略是所有主要浏览器的标准功能,并且已经存在多年。确实,使用非浏览器客户端或 Postman 等开发人员工具可以绕过这些保护,但恶意网站无法使用这些工具绕过 SOP 限制如果您知道任何例外情况,请分享。
2021-05-01 05:30:10
@ seansean11 当然,如果您只允许该内容类型,那么您就不需要 CSRF,但是 OP 专门询问了默认的 Rails 应用程序。
2021-05-07 05:30:10
@Sarkom 不一定是非浏览器客户端,但也可以在任何浏览器中绕过。绕过镀铬
2021-05-11 05:30:10

我不知道您面临的具体问题是什么。但是,如果您在新 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

如果需要认证,使用基本认证、令牌认证或 JWT

亲爱的@Abhi,我没有将 Rails 用于前端页面。我正在使用 Ember、Angular、React、Vue。
2021-05-02 05:30:10

我来回答一下问题。本文解释了全部细节: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 接受仅标头身份验证

如果您不使用 cookie 发送 session_id,那么您的 API 仅使用Authentication标头进行身份验证。那么你就不需要 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 不是问题,但您仍然可以防止跨站点脚本攻击,确保您的通信仅是 HTTPs,等等......

如果您使用 SPA 应用程序,那么您主要将 Rails 用作 API。CSRF 令牌是为服务器渲染而设计的……不是 SPA。在 SPA 中,您已经在身份验证期间使用了令牌,因此无需为 CSRF 使用另一个令牌。CSRF 旨在保护跨站点调用,但 API 本身的设计方式允许来自任何地方的请求,直到它们通过身份验证为止。

只需为您的 API 禁用它即可。我会使用一些API命名空间并设置一个BaseController,它将为所有 API 控制器继承。在那里你应该设置protect_from_forgery

class API::BaseController < ApplicationController
  protect_from_forgery with: :null_session
end
你怎么看这个:stackoverflow.com/a/15056471/4412054
2021-04-25 05:30:10
“CSRF 仅适用于服务器渲染的页面?” -> 是的;“但是,如果我除了 cookie 身份验证之外还有我的 API 呢?” -> 那么它只是一个ajax请求,而不是一个API方式(我的观点);“例如使用设计?” -> 设计有用于令牌认证的扩展 gem;“而我的客户只是我的 SPA?” -> 任何东西。移动设备/其他服务器...任何使用令牌进行身份验证或访问您的公共端点的东西;“我不想使用 JWT 或类似的东西。” -> 为什么?“所以我认为对于基于 cookie 的会话,我的 SPA 和 Rails API 也需要 CSRF,对吗?” -> 这会强制你每次都加载 CSRF
2021-05-03 05:30:10
我不明白。CSRF 仅适用于服务器渲染的页面?但是,如果我除了 cookie 身份验证之外还有我的 API 呢?例如使用设计?而我的客户只是我的 SPA?我不想使用 JWT 或类似的。所以我认为对于基于 cookie 的会话,我的 SPA 和 Rails API 也需要 CSRF,对吗?
2021-05-11 05:30:10
我不喜欢使用 JWT,因为所有这些原因:cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions
2021-05-14 05:30:10
你所说的ajax calls就是SPA框架(Ember、React、Angular)所称的API requests
2021-05-17 05:30:10