单页应用程序和CSRF令牌

时间:2018-05-03 16:29:16

标签: javascript ruby-on-rails reactjs session 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)请求?

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

CLARIFY

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

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

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

4 个答案:

答案 0 :(得分:5)

客户端(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令牌。

答案 1 :(得分:1)

如果你使用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

答案 2 :(得分:1)

我只是回答这个问题。本文将介绍完整的详细信息:  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在这种情况下不是问题,但您仍然可以防止跨站点脚本攻击,确保您的通信仅限HTTP,等等...

答案 3 :(得分:0)

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

最近我使用了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

它做了以下事情:

  1. 强制执行各种操作的确认对话框;
  2. 从超链接发出非GET请求;
  3. 使表单或超链接与Ajax异步提交数据;
  4. 提交按钮会在表单提交时自动禁用,以防止双击。 (来自:https://github.com/rails/rails-ujs/tree/master
  5. 但默认情况下,它不包括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