警告:无法验证 CSRF 令牌真实性轨道

WARNING: Can't verify CSRF token authenticity rails

提问人:kbaccouche 提问时间:8/26/2011 最后编辑:RyanSkbaccouche 更新时间:10/2/2023 访问量:175397

问:

我正在使用 AJAX 将数据从视图发送到控制器,我收到此错误:

警告:无法验证 CSRF 令牌的真实性

我想我必须发送这个带有数据的令牌。

有谁知道我该怎么做?

编辑:我的解决方案

我通过将以下代码放在 AJAX 帖子中来做到这一点:

headers: {
  'X-Transaction': 'POST Example',
  'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
},
Ruby-on-Rails jQuery CSRF

评论

7赞 Anatoly 8/26/2011
布局标题中是否有 <%= csrf_meta_tag %>
0赞 kbaccouche 8/26/2011
是的,像这样:<%= csrf_meta_tags %>
6赞 Anatoly 8/26/2011
您是否有提供 AJAX 客户端功能的 jQuery-Rails 库?
2赞 Ege Akpinar 7/26/2012
而 HAML 的方法是添加“= csrf_meta_tags”
0赞 MSC 7/9/2016
“X-Transaction”系列有什么作用?没有它似乎可以正常工作。

答:

22赞 auralbee 8/26/2011 #1

如果我没记错的话,您必须将以下代码添加到表单中,以解决此问题:

<%= token_tag(nil) %>

不要忘记参数。

评论

8赞 szeryf 5/17/2012
实际上,这应该是:.然后,您将获得自动生成的令牌。<%= token_tag(nil) %>
409赞 Chau Hong Linh 11/18/2011 #2

您应该这样做:

  1. 确保你的布局中有<%= csrf_meta_tag %>

  2. 添加到所有 ajax 请求中以设置标头,如下所示:beforeSend


$.ajax({ url: 'YOUR URL HERE',
  type: 'POST',
  beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))},
  data: 'someData=' + someData,
  success: function(response) {
    $('#someDiv').html(response);
  }
});

若要在所有请求中发送令牌,可以使用:

$.ajaxSetup({
  headers: {
    'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
  }
});

评论

37赞 James Conroy-Finn 9/2/2012
Rails 团队提供的 jQuery UJS 库会自动将 CSRF 令牌添加到 jQuery AJAX 请求中。自述文件包含有关如何进行设置的说明。github.com/rails/jquery-ujs/blob/master/src/rails.js#L91
1赞 Pieter Jongsma 2/24/2013
请注意,您可以使用 $.ajaxSetup 函数一次设置所有请求的标头。
1赞 cassi.lup 5/1/2013
非常好!寻找这个答案已经有一段时间了。无缝工作。谢谢!
0赞 Trip 4/14/2015
@JamesConroy-Finn:这在 Rails 4 中停止了工作。仍在试图找出原因。
4赞 Topher Fangio 9/13/2017
需要注意的是,如果您按照上述建议使用 jQuery UJS,则需要确保 rails-ujs include 位于 jquery include 之后,否则它将失败并出现与 op 相同的错误。
6赞 sciritai 11/18/2011 #3

我只是想我会在这里链接这个,因为这篇文章包含您正在寻找的大部分答案,而且也非常有趣

http://www.kalzumeus.com/2011/11/17/i-saw-an-extremely-subtle-bug-today-and-i-just-have-to-tell-someone/

31赞 ADAM 11/25/2011 #4

最好的方法实际上是直接在你的 rails 代码中打印出令牌。您不需要像其他帖子提到的那样使用 javascript 在 dom 中搜索 csrf 令牌。只需添加标题选项,如下所示;<%= form_authenticity_token.to_s %>

$.ajax({
  type: 'post',
  data: $(this).sortable('serialize'),
  headers: {
    'X-CSRF-Token': '<%= form_authenticity_token.to_s %>'
  },
  complete: function(request){},
  url: "<%= sort_widget_images_path(@widget) %>"
})

评论

7赞 Scott McMillin 11/29/2011
您可以向 $.ajaxSetup() 添加标头,而不是对每个 ajax 命令执行此操作。
1赞 opsidao 7/23/2012
我宁愿推荐使用这个答案......
18赞 radixhound 7/28/2012
我不太喜欢在 javascript 中使用 ERB 的方法。
0赞 sockmonk 4/4/2014
这迫使您使用 ERB 生成 javascript,这是非常有限的。即使有些地方 ERB 可能很合适,但也有其他地方不适合,仅仅为了获得代币而添加它也是一种浪费。
13赞 Danny 1/10/2012 #5

从较旧的应用程序升级到 rails 3.1,包括 csrf meta 标签仍然无法解决它。在 rubyonrails.org 博客上,他们提供了一些升级技巧,特别是这一行jquery,它应该放在布局的head部分:

$(document).ajaxSend(function(e, xhr, options) {
 var token = $("meta[name='csrf-token']").attr("content");
  xhr.setRequestHeader("X-CSRF-Token", token);
});

摘自这篇博文:http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails

就我而言,会话在每个ajax请求时都会被重置。添加上面的代码解决了这个问题。

3赞 Tim Scollick 4/26/2012 #6

如果您将 javascript 与 jQuery 一起使用以在表单中生成令牌,则有效:

<input name="authenticity_token" 
       type="hidden" 
       value="<%= $('meta[name=csrf-token]').attr('content') %>" />

显然,你需要在你的 Ruby 布局中加入它。<%= csrf_meta_tag %>

1赞 equivalent8 1/13/2013 #7

如果有人需要与 Uploadify 和 Rails 3.2 相关的帮助(就像我在谷歌上搜索这篇文章时一样),这个示例应用程序可能会有所帮助: https://github.com/n0ne/Uploadify-Carrierwave-Rails-3.2.3/blob/master/app/views/pictures/index.html.erb

还要检查此应用程序中的控制器解决方案

15赞 Walter Schreppers 5/6/2013 #8

确实是最简单的方法。不要为更改标题而烦恼。

确保您拥有:

<%= csrf_meta_tag %> in your layouts/application.html.erb

只需像这样做一个隐藏的输入字段:

<input name="authenticity_token" 
               type="hidden" 
               value="<%= form_authenticity_token %>"/>

或者,如果你想要一个jQuery ajax帖子:

$.ajax({     
    type: 'POST',
    url: "<%= someregistration_path %>",
    data: { "firstname": "text_data_1", "last_name": "text_data2", "authenticity_token": "<%= form_authenticity_token %>" },                                                                                  
    error: function( xhr ){ 
      alert("ERROR ON SUBMIT");
    },
    success: function( data ){ 
      //data response can contain what we want here...
      console.log("SUCCESS, data="+data);
    }
});

评论

0赞 Teemu Leisti 9/29/2014
将隐藏的输入字段添加到我的输入表单中为我解决了这个问题。
0赞 Jason FB 2/19/2019
如果你使用 Rails 的表单助手,这是自动完成的。
1赞 Mek 10/29/2013 #9

对于那些确实需要非jQuery答案的人,您可以简单地添加以下内容:

xmlhttp.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));

这里有一个非常简单的例子:

xmlhttp.open("POST","example.html",true);
xmlhttp.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));
xmlhttp.send();

评论

9赞 Yule 6/29/2015
这不会将jQuery用于选择器吗?
1赞 Jared Menard 10/5/2021
xmlhttp.setRequestHeader('X-CSRF-Token', document.querySelector('meta[name=“csrf-token”]').content);
6赞 Abhi 12/25/2013 #10

哎呀。。

我在应用程序.js中错过了以下行

//= require jquery_ujs

我更换了它,它的工作..

=======更新=========

5 年后,我又遇到了同样的错误,现在我有了全新的 Rails 5.1.6,我又找到了这篇文章。就像生命的循环一样。

现在的问题是: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令牌呢?如果有人对此有详细的了解,请评论我。我很感激。

无论如何,我在我的自定义 js 文件中添加了以下内容以使其工作(感谢其他答案来帮助我达到此代码):

$( document ).ready(function() {
  $.ajaxSetup({
    headers: {
      'X-CSRF-Token': Rails.csrfToken()
    }
  });
  ----
  ----
});

评论

0赞 Wylliam Judd 9/27/2017
这也是我需要做的。之所以出现这种情况,是因为我正在构建一个 React 应用程序来慢慢替换现有的 Rails 应用程序。由于现有应用程序中存在一堆 javascript 噪音,因此我创建了一个不同的布局来访问不同的 javascript 文件,但我未能包含 .这就是诀窍。jquery_ujs
1赞 Abhi 4/19/2018
是的,有时如果我们在返工时错过了这一点,请重构一些东西......很难找到出了什么问题。因为我们没有手动做任何事情来使它工作,所以 Rails 会自动包含它。我们认为它已经存在了。感谢这样的社交问答网站
0赞 5/7/2018
也许你可以回答这个类似的问题:stackoverflow.com/questions/50159847/......
6赞 Chezhian 4/8/2014 #11

您可以像下面这样全局编写它。

普通 JS:

$(function(){

    $('#loader').hide()
    $(document).ajaxStart(function() {
        $('#loader').show();
    })
    $(document).ajaxError(function() {
        alert("Something went wrong...")
        $('#loader').hide();
    })
    $(document).ajaxStop(function() {
        $('#loader').hide();
    });
    $.ajaxSetup({
        beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))}
    });
});

咖啡脚本:

  $('#loader').hide()
  $(document).ajaxStart ->
    $('#loader').show()

  $(document).ajaxError ->
    alert("Something went wrong...")
    $('#loader').hide()

  $(document).ajaxStop ->
    $('#loader').hide()

  $.ajaxSetup {
    beforeSend: (xhr) ->
      xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))
  }
7赞 dreadwail 11/23/2014 #12

这里得票最高的答案是正确的,但如果你正在执行跨域请求,则不会起作用,因为除非你明确告诉jQuery传递会话cookie,否则会话将不可用。操作方法如下:

$.ajax({ 
  url: url,
  type: 'POST',
  beforeSend: function(xhr) {
    xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))
  },
  xhrFields: {
    withCredentials: true
  }
});

评论

0赞 5/7/2018
请问你能不能回答这个非常相似的问题?stackoverflow.com/questions/50159847/......
10赞 nathanvda 7/8/2015 #13
  1. 确保你的布局中有<%= csrf_meta_tag %>
  2. 添加 a 以在 ajax 请求中包含 csrf-token 以设置标头。这仅对请求是必需的。beforeSendpost

读取 csrf-token 的代码可在 中找到,因此恕我直言,最简单的方法是使用它,如下所示:rails/jquery-ujs

$.ajax({
  url: url,
  method: 'post',
  beforeSend: $.rails.CSRFProtection,
  data: {
    // ...
  }
})

评论

0赞 jiehanzheng 2/1/2017
简单,无需重新实现 Rails 已经包含的内容。这应该是选定的答案。
0赞 olhor 3/16/2018
在 Rails 5.1 中也可以:headers: { 'X-CSRF-Token': Rails.csrfToken() }
0赞 5/7/2018
@nathanvda,也许你可以回答这个类似的问题:stackoverflow.com/questions/50159847/......
0赞 Neil Stockbridge 11/12/2015 #14

我正在使用 Rails 4.2.4,但无法弄清楚为什么我会得到:

Can't verify CSRF token authenticity

我在布局中:

<%= csrf_meta_tags %>

在控制器中:

protect_from_forgery with: :exception

调用显示正在设置的标头(尽管不需要设置标头 - 它已经完成了):tcpdump -A -s 999 -i lo port 3000ajaxSetup

X-CSRF-Token: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
DNT: 1
Content-Length: 125
authenticity_token=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

最后它失败了,因为我关闭了 cookie。 如果不启用 cookie,CSRF 将无法工作,因此如果您看到此错误,这是另一个可能的原因。

评论

0赞 joehwang 12/26/2018
这是非常有帮助的!
6赞 svnm 9/5/2016 #15

如果您不使用 jQuery 并且使用像 fetch API 这样的东西来处理请求,您可以使用以下命令来获取:csrf-token

document.querySelector('meta[name="csrf-token"]').getAttribute('content')

fetch('/users', {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content')},
    credentials: 'same-origin',
    body: JSON.stringify( { id: 1, name: 'some user' } )
    })
    .then(function(data) {
      console.log('request succeeded with JSON response', data)
    }).catch(function(error) {
      console.log('request failed', error)
    })

评论

0赞 5/7/2018
请问你能不能回答这个非常相似的问题?stackoverflow.com/questions/50159847/......
4赞 swordray 3/31/2017 #16

使用 jquery.csrf (https://github.com/swordray/jquery.csrf)。

  • Rails 5.1 或更高版本

    $ yarn add jquery.csrf
    
    //= require jquery.csrf
    
  • Rails 5.0 或更早版本

    source 'https://rails-assets.org' do
      gem 'rails-assets-jquery.csrf'
    end
    
    //= require jquery.csrf
    
  • 源代码

    (function($) {
      $(document).ajaxSend(function(e, xhr, options) {
        var token = $('meta[name="csrf-token"]').attr('content');
        if (token) xhr.setRequestHeader('X-CSRF-Token', token);
      });
    })(jQuery);
    

评论

0赞 Damien Justin Šutevski 5/13/2017
在 5.1 中使用 webpack,是行不通的,对吧?取而代之的是,我使用了 pack js 文件。请注意,您不需要将其包含在视图中的包标记中。//= require jquery.csrfimport 'jquery.csrf'
3赞 Hoffmann 2/6/2018 #17

我在这个问题上挣扎了好几天。任何 GET 调用都正常工作,但所有 PUT 都会生成“无法验证 CSRF 令牌真实性”错误。在我向nginx添加SSL证书之前,我的网站运行良好。

我终于在我的nginx设置中偶然发现了这一行:

location @puma { 
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
    proxy_set_header Host $http_host; 
    proxy_redirect off;
    proxy_set_header X-Forwarded-Proto https;   # Needed to avoid 'WARNING: Can't verify CSRF token authenticity'
    proxy_pass http://puma; 
}

添加缺失的行“proxy_set_header X-Forwarded-Proto https;”后,我所有的 CSRF 令牌错误都退出了。

希望这能帮助到其他也在用头撞墙的人。哈哈

0赞 legoblocks 10/2/2023 #18

在 2023 年升级到 Rails 7 后,由于某种原因我遇到了这个问题。也许我使用的是普通的jQuery,而不是jQuery的某些rails变体,或者任何“UJS”。

通过将此添加到我的特定于页面的 Javascript 代码中来修复它:

    // Add the CSRF token to all ajax requests                                                                                                                      
    $.ajaxSetup({                                                                                                                                                   
      headers: {                                                                                                                                                    
        'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')                                                                                                
      }                                                                                                                                                             
    });                                                                                                                                                             

(我已经在我的html布局的顶部)<%= csrf_meta_tags %>

我的普通jQuery Ajax代码如下所示:

      var jqxhr = $.post( "something/example", { color: 'red' })
        .done(function(data) {
          console.log( "Ajax success:");
          console.log( data );
        })
        .fail(function(data) {
          console.log( "Ajax error:");
          console.log( data );
        })
        .always(function() {
          console.log( "Ajax finished" );
        });