【问题标题】:rails - "WARNING: Can't verify CSRF token authenticity" for json devise requestsrails - json设计请求的“警告:无法验证CSRF令牌真实性”
【发布时间】:2012-03-10 21:34:09
【问题描述】:

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

我知道出于安全原因Rails is checking the CSRF token 在所有请求类型(包括 JSON/XML)上。

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

这个类似(仍然不被接受)answer 建议

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

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

【问题讨论】:

    标签: ruby-on-rails-3 ruby-on-rails-3.1 devise csrf


    【解决方案1】:

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

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

    【讨论】:

    • 感谢您的建议。我试过了,但我仍然有同样的警告WARNING: Can't verify CSRF token authenticity
    【解决方案2】:

    编辑

    在 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 帖子/放置。

    例如,在 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]]];
    

    【讨论】:

    • 如果攻击者将格式标记为“json”,您将面临攻击。这就是为什么 Rails 根据定义发出警告的原因。我想让我的 json 能够正确传递令牌,否则在日志中出现警告就可以了。
    • 您会受到攻击,这就是为什么他们建议您在关闭过滤器时设置一些其他安全措施。通常对于 API 请求,请求者会发送一个 API 密钥以及发布数据,然后您将在执行所需的方法之前对其进行验证。
    • 在 Rails 4 中,您可以执行以下操作:protect_from_forgery with: :null_session, :if => Proc.new { |c| c.request.format == 'application/json' }
    • @genkilabs 我喜欢这个,添加到 Rails 4 用户的回答中,谢谢 =]
    • 为了在我的 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];
    【解决方案3】:

    令人担忧的是,在 Rails 3.2.3 中,我们现在在 production.log 中收到 CSRF 警告,但帖子并没有失败!我希望它失败,因为它可以保护我免受攻击。您可以在过滤器 btw 之前使用 jquery 添加 csrf 令牌:

    http://jasoncodes.com/posts/rails-csrf-vulnerability

    【讨论】:

      【解决方案4】:

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

      例如,把它放在你的会话中#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

      【讨论】:

        【解决方案5】:

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

        确保你有:

        <%= 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 标签的帖子,它只会发布警告并提交数据就好了......
        • 隐藏真实性令牌输入字段的较短方法是= token_tag(nil)(nil 很重要)
        • 在旧版本的 rails 中,标签以复数结尾。
        【解决方案6】:

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

        现在它确实有一个有效的 csrf-token :) 希望对你有帮助

        【讨论】:

          【解决方案7】:

          也适用于开发/测试模式。

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

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

          protect_from_forgery
          

          【讨论】:

            【解决方案8】:

            我已经使用了以下内容。使用包含?所以如果内容类型是 application/json;charset=utf-8 那么它仍然可以工作。

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

            【讨论】:

              【解决方案9】:

              我通过这种方式解决了这个错误:

              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。
              • 谢谢!需要一种快速(且肮脏)的方法来解决这个问题,这对于我想要实现的目标来说可能足够好。
              • 链接文档(现已针对 Rails 5 更新)似乎是:protect_from_forgery with: :exception, unless: -&gt; { request.format.json? },它对我有用。谢谢。
              • 这只是禁用检查,从而避免了问题。如果和应用程序要禁用此功能,那么打开防伪保护有什么意义?
              【解决方案10】:

              This answer 更好。

              在发送任何 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);
                  };
              }());
              

              【讨论】:

                猜你喜欢
                • 2013-05-30
                • 2013-06-16
                • 2017-07-19
                • 2011-11-04
                • 2011-11-15
                • 2012-04-27
                • 2017-01-01
                相关资源
                最近更新 更多