rails-“警告:无法验证CSRF令牌的真实性”用于json devise请求


82

如何检索CSRF令牌以通过JSON请求传递?

我知道出于安全原因,Rails正在检查所有请求类型(包括JSON / XML)上的CSRF令牌

我可以放入控制器skip_before_filter :verify_authenticity_token,但是会失去CRSF保护(不建议:-))。

这个类似的(仍然不被接受)的答案表明

使用检索令牌 <%= form_authenticity_token %>

问题是如何?我是否需要先调用任何页面以检索令牌,然后使用Devise进行真正的身份验证?还是我可以从服务器上获得一次信息然后再使用它(直到我在服务器本身上手动更改它)?

Answers:


127

编辑

在Rails 4中,我现在使用@genkilabs在以下注释中建议的内容:

protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format == 'application/json' }

它可以完全关闭内置的安全性,但是可以在没有CSRF令牌的情况下击中服务器时杀死可能存在的任何会话。


skip_before_filter :verify_authenticity_token, :if => Proc.new { |c| c.request.format == 'application/json' }

这将关闭CSRF检查是否已正确标记为json帖子/ puts。

例如,在iOS中,将以下内容设置为您的NSURLRequest,其中“参数”是您的参数:


[request setHTTPMethod:@"POST"];

[request setValue:@"application/json" 
       forHTTPHeaderField:@"content-type"];

[request setValue:@"application/json" 
       forHTTPHeaderField:@"accept"];

[request setHTTPBody:[NSData dataWithBytes:[parameters UTF8String] 
                                            length:[parameters length]]];

3
如果攻击者将格式标记为“ json”,则您可能会遭受攻击。这就是Rails定义发出警告的原因。我希望我的json能够正确传递令牌,否则在日志中显示警告就可以了。

21
您容易受到攻击,这就是为什么他们建议您在关闭过滤器时还具有其他安全措施。通常,对于API请求,请求者将发送API密钥以及发布数据,然后在执行所需方法之前进行验证。
瑞安·克鲁斯

18
在rails 4中,您可以执行以下操作:protect_from_forgery with: :null_session, :if => Proc.new { |c| c.request.format == 'application/json' }
genkilabs

2
@genkilabs我喜欢,添加了它来回答Rails 4用户,谢谢=]
Ryan Crews 2014年

1
我刚才为在Rails&iOS应用程序中实现此目的做了两件事:1)我没有看到我们在Ruby中混合使用旧式和新式哈希语法的任何原因,因此控制器中的代码如下:protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format == 'application/json' }2)与这个问题无关,但与从iOS发布JSON有关,因为我正在使用AFNetworking并且Rails不会自动包装参数,所以我做了以下工作:manager.requestSerializer = [AFJSONRequestSerializer serializer];
mharper 2014年

18

成功登录后,您可以使用自定义标头发送CSRF令牌。

例如,将其放在您的session#create中:

response.headers['X-CSRF-Token'] = form_authenticity_token

提供CSRF令牌的示例登录响应标头:

HTTP/1.1 200 OK
Cache-Control: max-age=0, private, must-revalidate
Connection: Keep-Alive
Content-Length: 35
Content-Type: application/json; charset=utf-8
Date: Mon, 22 Oct 2012 11:39:04 GMT
Etag: "9d719d3b9aabd413c3603e04e8a3933d"
Server: WEBrick/1.3.1 (Ruby/1.9.3/2012-10-12)
Set-Cookie: [cut for readability] 
X-Csrf-Token: PbtMPfrszxH6QfRcWJCCyRo7BlxJUPU7HqC2uz2tKGw=
X-Request-Id: 178746992d7aca928c876818fcdd4c96
X-Runtime: 0.169792
X-Ua-Compatible: IE=Edge

该令牌有效,直到您再次登录或(如果您通过API支持此操作,则注销)。您的客户端可以从登录响应标头中提取并存储令牌。然后,每个POST / PUT / DELETE请求都必须使用登录时收到的值来设置X-CSRF-Token标头。

具有CSRF令牌的示例POST标头:

POST /api/report HTTP/1.1
Accept: application/json
Accept-Encoding: gzip, deflate, compress
Content-Type: application/json; charset=utf-8
Cookie: [cut for readability]
Host: localhost:3000
User-Agent: HTTPie/0.3.0
X-CSRF-Token: PbtMPfrszxH6QfRcWJCCyRo7BlxJUPU7HqC2uz2tKGw=

文档:form_authenticity_token


18

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

确保您具有:

<%= csrf_meta_tag %>

在你的 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);
    }
});

基本上,当您发布json数据时,只需在数据中添加一个有效的authenticity_token字段,post警告就会消失...


恕我直言,它不仅应该在记录器中发出警告,而且默认情况下完全丢弃没有有效csrf标签的帖子,它只是发布警告并提交数据就可以了……
Walter Schreppers

做隐藏的真实性令牌输入字段的较短方法是= token_tag(nil)(无重要意义)
Yo Ludke 2015年

在旧版本的rails中,标签以复数结尾。<%= csrf_meta_tags%>
凌晨

4

我这样解决了该错误:

class ApplicationController < ActionController::Base
  protect_from_forgery
  skip_before_action :verify_authenticity_token, if: :json_request?

  protected

  def json_request?
    request.format.json?
  end
end

来源:http//api.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html


我不确定您是否要这样做-浏览器可以使用JSON API。
乔·范戴克

谢谢!需要一种快速(又肮脏)的方法来解决此问题,这对于我想要实现的目的可能已经足够好了。
NerdyTherapist,2016年

链接的文档(现已针对Rails 5更新)似乎是:protect_from_forgery with: :exception, unless: -> { request.format.json? }它对我有用。谢谢。
akostadinov

1
这只是禁用了检查,从而避免了该问题。如果and app要禁用此功能,那么启用防伪保护的意义何在?
jefflunt


2

我用下面的。使用包含?因此,如果内容类型为application / json; charset = utf-8,则它仍在工作。

protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format.include? 'application/json' }

2

这个答案更好。

在发送任何XMLHttpRequest之前,您无需做任何额外的工作即可保持CSRF-TOKEN验证(附加令牌)。没有JQuery,没有任何东西只是复制/粘贴和刷新。

只需添加此代码。

(function() {
    var send = XMLHttpRequest.prototype.send,
        token = $('meta[name=csrf-token]').attr('content');
    XMLHttpRequest.prototype.send = function(data) {
        this.setRequestHeader('X-CSRF-Token', token);
        return send.apply(this, arguments);
    };
}());

1

对于以下版本的Rails,我也遇到了同样的问题
:gem'rails',:git =>'git://github.com/rails/rails.git',:branch =>'3-2-stable'

我更新到3.2.2,现在一切正常。:)
gem'rails','3.2.2'


谢谢你的建议。我尝试了一下,但是仍然有相同的警告WARNING: Can't verify CSRF token authenticity

0

今晚我遇到了同样的问题。发生这种情况的原因是,当您登录最后一个csrf令牌时,该令牌不再有效。我所做的是: $("meta[name=csrf-token]").attr('content', '<%= form_authenticity_token %>');在您的app / views / devise / sessions / create.js.rb中。

现在它确实具有有效的csrf令牌了:)我希望它会有所帮助


0

也用于开发/测试模式。

protect_from_forgery with: :exception unless %w(development test).include? Rails.env

显示此警告是因为您正在使用:null_session,在Rails 4.1中,如果未with:指定任何选项,则默认情况下它将起作用。

protect_from_forgery
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.