rails - 用于 json/xml 请求的 InvalidAuthenticityToken

Posted

技术标签:

【中文标题】rails - 用于 json/xml 请求的 InvalidAuthenticityToken【英文标题】:rails - InvalidAuthenticityToken for json/xml requests 【发布时间】:2010-11-17 18:04:20 【问题描述】:

由于某种原因,在使用 json 或 xml 向我的应用程序发出 post 请求时,我得到了 InvalidAuthenticityToken。我的理解是,rails 应该只对 html 或 js 请求需要一个真实性令牌,因此我不应该遇到这个错误。到目前为止,我发现的唯一解决方案是对我想通过 API 访问的任何操作禁用protect_from_forgery,但由于显而易见的原因,这并不理想。想法?

    def create
    respond_to do |format|
        format.html
        format.json
            render :json => Object.create(:user => @current_user, :foo => params[:foo], :bar => params[:bar])
        
        format.xml
            render :xml => Object.create(:user => @current_user, :foo => params[:foo], :bar => params[:bar])
        
    end
end

当我将请求传递给操作时,这就是我在日志中得到的内容:

 Processing FooController#create to json (for 127.0.0.1 at 2009-08-07 11:52:33) [POST]
 Parameters: "foo"=>"1", "api_key"=>"44a895ca30e95a3206f961fcd56011d364dff78e", "bar"=>"202"

ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):
  thin (1.2.2) lib/thin/connection.rb:76:in `pre_process'
  thin (1.2.2) lib/thin/connection.rb:74:in `catch'
  thin (1.2.2) lib/thin/connection.rb:74:in `pre_process'
  thin (1.2.2) lib/thin/connection.rb:57:in `process'
  thin (1.2.2) lib/thin/connection.rb:42:in `receive_data'
  eventmachine (0.12.8) lib/eventmachine.rb:242:in `run_machine'
  eventmachine (0.12.8) lib/eventmachine.rb:242:in `run'
  thin (1.2.2) lib/thin/backends/base.rb:57:in `start'
  thin (1.2.2) lib/thin/server.rb:156:in `start'
  thin (1.2.2) lib/thin/controllers/controller.rb:80:in `start'
  thin (1.2.2) lib/thin/runner.rb:174:in `send'
  thin (1.2.2) lib/thin/runner.rb:174:in `run_command'
  thin (1.2.2) lib/thin/runner.rb:140:in `run!'
  thin (1.2.2) bin/thin:6
  /opt/local/bin/thin:19:in `load'
  /opt/local/bin/thin:19

【问题讨论】:

【参考方案1】:

从 Rails 4.2 开始,我们有另一种方法是在 Rails 应用程序中使用 skip_before_filter 来避免 verify_authenticity_token:

skip_before_action :verify_authenticity_token, only: [:action1, :action2]

这会让 curl 完成它的工作。

Ruby on Rails 4.2 发行说明:https://guiarails.com.br/4_2_release_notes.html

【讨论】:

这增加了一个安全问题。 verify_authenticity_token 运行是有原因的。【参考方案2】:

只要 javascript 存在于 Rails 服务的网站上(例如:JS sn-p;或通过 webpacker 管理的 React 应用程序),您就可以默认使用 application.html.erb 中包含的 csrf_meta_tags 中的值。

application.html.erb:

<html>
  <head>
    ...
    <%= csrf_meta_tags %>
    ...

因此在您网站的 HTML 中:

<html>
  <head>
    <meta name="csrf-token" content="XZY">

content 属性中获取令牌并在请求中使用它:

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

const response = await fetch("/entities/1", 
  method: 'PATCH',
  headers: 
    'Content-Type': 'application/json'
  ,
  body: JSON.stringify( authenticity_token: token, entity:  name: "new name"  )
);

这类似于@andrewle 的回答,但不需要额外的令牌。

【讨论】:

【参考方案3】:

这与@user1756254's answer 相同,但在 Rails 5 中您需要使用更多不同的语法:

protect_from_forgery unless: ->  request.format.json? 

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

【讨论】:

这不是安全风险吗? @WylliamJudd 不,仅为 JSON 禁用它不会带来安全风险,因为防伪目标是保护从其他站点提交的 HTML 表单 当官方文档说:'如果你正在构建一个API你应该改变ApplicationController中的伪造保护方法(默认情况下::exception)',那么它就足够安全了 这看起来是个非常糟糕的主意,如果你的 cors 允许来自其他域的请求,或者攻击者在允许的域之一上有 js,那么攻击者可以发送 CSRF 请求。摘要:请不要这样做,您的请求应该传递自定义令牌来验证用户,或者从元标记中获取 csrf 令牌 这里的文档有点粗心。 “我们可能希望禁用 API 的 CSRF 保护,因为它们通常设计为无状态。也就是说,请求 API 客户端将为您处理会话,而不是 Rails。”因此,如果您以其他方式对 API 请求进行身份验证,则可以禁用此功能,但不要这样做,例如,在您的应用中允许来自 JS sn-p 的经过身份验证的请求。查看我的回复。【参考方案4】:

启用protect_from_forgery 后,Rails 需要一个真实性令牌来处理任何非 GET 请求。 Rails 会自动将真实性令牌包含在使用表单助手创建的表单或使用 AJAX 助手创建的链接中——因此在正常情况下,您不必考虑它。

如果您没有使用内置的 Rails 表单或 AJAX 助手(也许您正在做不引人注目的 JS 或使用 JS MVC 框架),您必须自己在客户端设置令牌并发送它在提交 POST 请求时连同您的数据一起。您可以在布局的&lt;head&gt; 中添加这样的一行:

<%= javascript_tag "window._token = '#form_authenticity_token'" %>

然后您的 AJAX 函数会将令牌与您的其他数据一起发布(以 jQuery 为例):

$.post(url, 
    id: theId,
    authenticity_token: window._token
);

【讨论】:

Optimate 应该接受这个答案。顺便说一句,如果您使用$.ajax,则需要将真实性令牌放在数据字段中,如下所示:$.ajax( data: authenticity_token: window._token )【参考方案5】:

要补充费尔南多的答案,如果您的控制器同时响应 json 和 html,您可以使用:

      skip_before_filter :verify_authenticity_token, if: :json_request?

【讨论】:

这不是安全风险吗? 是的。不要这样做。【参考方案6】:

添加到 andymism 的答案,您可以使用它在每个 POST 请求中应用默认包含 TOKEN:

$(document).ajaxSend(function(event, request, settings) 
    if ( settings.type == 'POST' ||  settings.type == 'post') 
        settings.data = (settings.data ? settings.data + "&" : "")
            + "authenticity_token=" + encodeURIComponent( window._token );
    
);

【讨论】:

【参考方案7】:

我遇到了类似的情况,问题是我没有通过正确的内容类型标头发送 - 我请求的是text/json,而我应该请求的是application/json

我使用curl 测试我的应用程序(根据需要进行修改):

curl -H "Content-Type: application/json" -d '"person": "last_name": "Lambie","first_name": "Matthew"' -X POST http://localhost:3000/people.json -i

或者您可以将 JSON 保存到本地文件并像这样调用curl

curl -H "Content-Type: application/json" -v -d @person.json -X POST http://localhost:3000/people.json -i

当我将内容类型标题更改为右侧 application/json 时,我所有的麻烦都消失了,我不再需要禁用伪造保护。

【讨论】:

这对 Web 应用程序实际上并不适用。浏览器无法运行 curl。 =( 重点是我用 curl 调试了问题,发现发送正确的 Content-Type 标头很重要。

以上是关于rails - 用于 json/xml 请求的 InvalidAuthenticityToken的主要内容,如果未能解决你的问题,请参考以下文章

ajaxSetup 不适用于 Rails / jquery-ujs

Rails、redis 和 node.js 如何异步处理请求?

Ajax PUT 请求导致 Rails 应用程序注销。为啥?

ruby 用于测试Rails应用程序的内存使用情况的脚本。它将针对指定的操作运行30个请求并报告f

ruby 用于测试Rails应用程序的内存使用情况的脚本。它将针对指定的操作运行30个请求并报告f

GET 和 POST 请求的相同 Rails 4 路由