可以'即使设置了头,也无法验证CSRF令牌的真实性Rails 4 Ajax

Can't verify CSRF token authenticity Rails 4 Ajax even when header is set

本文关键字:令牌 CSRF 验证 真实性 Ajax Rails 设置 可以      更新时间:2023-09-26

我真的有问题,在这种情况下,我既不想跳过verify_authenticity_token过滤器,也不想更改为protect_from_forgery with: :null_session

在我的请求方法中,我使用csrf令牌设置一个标头,如下所示:

var token = document.querySelector("meta[name='csrf-token']").content;
xhr.setRequestHeader("X-CSRF-Token", token);

通过在我的控制器中插入一个断点,如下所示:

def verify_authenticity_token
  binding.pry
  super
end

我已验证标题已设置:

[1] pry(#<MyController>)> request.headers
=> #<ActionDispatch::Http::Headers:0x007fb227cbf490
 @env=
  {"CONTENT_LENGTH"=>"202",
   .
   .
   .
   # omitted headers
   .
   .
   .
   "HTTP_X_CSRF_TOKEN"=>"the-correct-token-from-meta-tag",
   .
   .
   .
  }

我还尝试将令牌作为带有关键字authenticity_token的参数传递(与Rails表单一样),并将X-CSRF-Param标记设置为匹配(来自meta[name="csrf-param"])。

然而,我仍然得到:

Can't verify CSRF token authenticity
Completed 422 Unprocessable Entity in 14638ms
ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken

有人见过这个吗?有什么想法可能导致这种情况吗?

提前感谢!

编辑:

根据marflar回答的评论中的讨论,在发出请求时,令牌似乎已经过期(通过与form_authenticity_token进行比较进行测试)。这让我更加困惑,因为<%= csrf_meta_tags %>中设置的令牌在下一个请求到来时已经过期。有什么想法吗?

第二版:根据下面marflar的建议,我在我的应用程序控制器中添加了以下after_filter

def set_csrf_headers
  response.headers['X-CSRF-Param'] = request_forgery_protection_token.to_s
  response.headers['X-CSRF-Token'] = form_authenticity_token
end

我在请求方法中更新了xhr.onload,如下所示:

namespace.request = = function (type, url, opts, callback) {
// code omitted
  xhr.onload = function () {
    setCSRFHeaders(xhr);
    var res = {data: JSON.parse(xhr.response), status: xhr.status};
    return callback.call(xhr, null, res);
  };
// code omitted
}
function setCSRFHeaders ( xhr ) {
  var csrf_param = xhr.getResponseHeader('X-CSRF-Param');
  var csrf_token = xhr.getResponseHeader('X-CSRF-Token');
  if (csrf_param) {
    document.querySelector("meta[name='csrf-param']").content = csrf_param;
  }
  if (csrf_token) {
    document.querySelector("meta[name='csrf-token']").content = csrf_token;
  }
}

我验证了响应标头和元标记是否正确重置,但是,当下一个请求到来时,这个新令牌再次过期。想法?

我也有同样的问题。我检查了Rails的源代码,并得出下一个结论:

  • authenticity_token本身未过期,因此无需在每次向服务器发出ajax请求后更新
  • 不需要同时发送params[:authenticity_token]header['x-csrf-token'],只需其中一个,rails将首先检查params,而不是header
  • 在页面刷新时,authenticity_token会有所不同,但这并不重要,因为它每次都是用一个时间板生成的,并且真正的csrf令牌(在服务器上)与时间无关
  • 保存在session[:_csrf_token]中的真实csrf令牌

正如你所看到的,令牌被保存在会话中,我的问题是我的会话在24小时后过期(可能用户在页面上停留了一天而没有刷新)

如果用户是通过cookie或其他一些令牌参数登录的,那么他无论如何都会获得新的会话,并由此生成新的CSRF令牌,任何具有旧authenticity_token的请求都将无效。

所以,主要的问题是会话,它已经过期或重置。

我的猜测是Rails可能希望标记在HTML中,而不是头中。你能试试吗?希望能有所帮助。

实际上,我认为您可能使用了一个过时的CRSF令牌,因为您正在从模板中复制它。

我通常在我的控制器操作中这样设置:

response.headers['X-CSRF-Param'] = "#{request_forgery_protection_token}"
response.headers['X-CSRF-Token'] = "#{form_authenticity_token}"

您页面中的令牌与调用form_authenticity_token返回的令牌匹配吗?

更新

针对您的评论(引用如下):

我刚刚检查过,你说得对,这是一个过时的代币,不幸的是,这让我更加困惑。带有CSRF数据的元标签是在初始页面加载时设置的,此时它们与form_authenticaity_token匹配,但在发出第一个ajax请求时,令牌已经过时。因此,无论我是在HTML中设置它们,还是将它们设置为标头,都无关紧要,因为这会同时发生,因此会遇到同样的问题,令牌在发出下一个请求之前过期。谢谢你到目前为止的帮助,有什么想法吗?

我在实现AJAX登录时遇到了这种问题。我发现登录后无法发出任何形式的POST请求,我需要以下代码来刷新我的令牌:

var update_csrf_token_and_param_after_ajax_login = function() {
  $(document).on("ajaxComplete", function(event, xhr, settings) {
    var csrf_param = xhr.getResponseHeader('X-CSRF-Param');
    var csrf_token = xhr.getResponseHeader('X-CSRF-Token');
    if (csrf_param) {
      $('meta[name="csrf-param"]').attr('content', csrf_param);
    }
    if (csrf_token) {
      $('meta[name="csrf-token"]').attr('content', csrf_token);
    }
  });
}

我认为您可能只需要在执行POST之前在页面中写入一个新的令牌。。。