我需要使用单页应用程序(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令牌仍然适合该会话。那么,如何使前一个令牌无效?我必须吗?
因为只有我无效才有意义,对吧?
答案 0 :(得分:5)
您只需每次会话获取CSRF令牌一次。您可以在浏览器中保留它并在每个(非GET)请求上发送它。
Rails似乎会在每个请求上生成一个新的CSRF令牌,但它会接受来自该会话的任何生成的令牌。实际上,它只是为每个请求使用一次性填充屏蔽单个令牌,以防止SSL BREACH攻击。更多细节见https://stackoverflow.com/a/49783739/2016618。您无需跟踪/存储这些令牌。
我强烈建议使用Rails的protect_from_forgery
指令,而不是自己在头文件中编码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
CSRF令牌在会话期间有效。因此,只需在登录后的会话期间生成一个CSRF令牌即可。
class LoginController < ApplicationController
def create
if user_password_match
# ....
cookie[:my_csrf_token]= form_authenticity_token
end
end
end
或者您可以像提议
一样刷新cookieclass 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"
如果您没有使用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
它做了以下事情:
但默认情况下,它不包括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