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 请求时连同您的数据一起。您可以在布局的<head>
中添加这样的一行:
<%= 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