Rails:发出 POST 请求时无法验证 CSRF 令牌的真实性
Posted
技术标签:
【中文标题】Rails:发出 POST 请求时无法验证 CSRF 令牌的真实性【英文标题】:Rails: Can't verify CSRF token authenticity when making a POST request 【发布时间】:2016-05-12 21:40:42 【问题描述】:我想将POST request
发送给我的本地开发人员,如下所示:
HTTParty.post('http://localhost:3000/fetch_heroku',
:body => :type => 'product',)
但是,它会从服务器控制台报告
Started POST "/fetch_heroku" for 127.0.0.1 at 2016-02-03 23:33:39 +0800
ActiveRecord::SchemaMigration Load (0.0ms) SELECT "schema_migrations".* FROM "schema_migrations"
Processing by AdminController#fetch_heroku as */*
Parameters: "type"=>"product"
Can't verify CSRF token authenticity
Completed 422 Unprocessable Entity in 1ms
这是我的控制器和路由设置,非常简单。
def fetch_heroku
if params[:type] == 'product'
flash[:alert] = 'Fetch Product From Heroku'
Heroku.get_product
end
end
post 'fetch_heroku' => 'admin#fetch_heroku'
我不确定我需要做什么?关闭 CSRF 肯定会起作用,但我认为创建这样的 API 时应该是我的错误。
我还需要做其他设置吗?
【问题讨论】:
对于 API,通常接受关闭 CSRF 令牌验证。我用protect_from_forgery with: :null_session
。
【参考方案1】:
跨站请求伪造 (CSRF/XSRF) 是指恶意网页诱使用户执行非预期的请求,例如通过使用小书签、iframe 或仅通过创建视觉上相似到足以欺骗用户的页面。
Rails CSRF protection 是为“经典”网络应用程序设计的 - 它只是提供一定程度的保证,即请求来自您自己的网络应用程序。 CSRF 令牌就像一个只有你的服务器知道的秘密——Rails 生成一个随机令牌并将其存储在会话中。您的表单通过隐藏的输入发送令牌,Rails 会验证任何非 GET 请求是否包含与会话中存储的内容匹配的令牌。
然而,API 通常被定义为跨站点,并且不仅仅用于您的网络应用程序,这意味着 CSRF 的整个概念并不完全适用。
相反,您应该使用基于令牌的策略来使用 API 密钥和机密对 API 请求进行身份验证,因为您正在验证请求来自已批准的 API 客户端,而不是来自您自己的应用。
您可以按照@dcestari 的指示停用 CSRF:
class ApiController < ActionController::Base
protect_from_forgery with: :null_session
end
更新。在 Rails 5 中,您可以使用 --api
选项生成仅限 API 的应用程序:
rails new appname --api
它们不包括 CSRF 中间件和许多其他多余的组件。
http://guides.rubyonrails.org/security.html#cross-site-request-forgery-csrf https://labs.kollegorna.se/blog/2015/04/build-an-api-now/ WARNING: Can't verify CSRF token authenticity rails【讨论】:
谢谢,我选择关闭部分 CSRF:***.com/questions/5669322/… 这表明所有 API 都服务于应用程序到应用程序的流量(其中向单个合作伙伴颁发唯一的密钥和秘密)。在那种情况下,就像服务器到服务器的通信一样,这个答案是合适的。然而,让大多数 Web 应用程序开发人员感到困惑的是,对于由您控制和编写的 javascript 客户端,您不想使用单个密钥(这会将单个密钥公开给所有客户端)。相反,Rail 的 CSRF 和 cookie 会话机制工作得很好——即使对于使用你的 API 的 Javascript 应用程序——如果你在每个请求中将 CSRF 令牌传递回 Rails。 在此 SO 帖子 ***.com/questions/7203304/… 中描述了您在 AJAX 中执行此操作的方式 @JasonFB 你是对的,因为javascript客户端的单一秘密不起作用。但是,如果您打算构建一个也可供其他类型的非浏览器客户端使用的 API,那么使用会话和 Rails CSRF 保护仍然存在问题。 如果您提供或构建 CSRF 的替代方案,请成为我的客人。只需知道要替换的原因,就知道用什么来替换它是合适的。显然,现在我们的狡辩变成了上下文。【参考方案2】:关闭不会呈现空会话的 CSRF 的另一种方法是添加:
skip_before_action :verify_authenticity_token
在您的 Rails 控制器中。这将确保您仍然可以访问会话信息。
同样,请确保您只在 API 控制器或其他不太适用 CSRF 保护的地方执行此操作。
【讨论】:
这和protect_from_forgery except: [:my_method_name]
一样吗?【参考方案3】:
api.rubyonrails.org 上有关于 API 控制器的 CSRF 配置的相关信息:
⋮
请务必记住,XML 或 JSON 请求也会受到影响,如果您正在构建 API,您应该更改
ApplicationController
中的防伪方法(默认情况下::exception
):class ApplicationController < ActionController::Base protect_from_forgery unless: -> request.format.json? end
我们可能希望对 API 禁用 CSRF 保护,因为它们通常设计为无状态。也就是说,请求API 客户端将为您处理会话,而不是 Rails。
⋮
【讨论】:
这太令人困惑了。我在说protect_from_forgery
对于 API 仍然是必要的消息来源和说它不是的消息来源之间摇摆不定。如果 API 用于单页应用程序,使用会话 cookie 进行用户身份验证怎么办?
CSRF 机制是 Rails 使用会话 cookie 处理攻击向量的内置方法。该机制保护您的控制器,同时在 Rails 会话 cookie 旁边(实际上是在内部)运行。如果没有protect_from_forgery,或者将其关闭或设置一个except,你就是在告诉Rails 不要使用来自CSRF 令牌(取自会话cookie)的信息来保护该操作。
我认为令人困惑的是,对于 Rails 文档,“API”是指一个服务器到服务器的应用程序,它将接收来自受信任的远程合作伙伴(不是所有访问您网站的 Web 用户)的 API 请求。对于这些人,请通过安全的不可破解机制提供唯一的密钥-秘密对。对于现代 Web 应用程序,许多人将通过 Web 客户端加载您的 Web 应用程序,您仍然使用基于会话或令牌的机制来唯一标识访问您网站的每个人。因此,除非您使用其他机制来执行此操作(Json Web 令牌等),否则请坚持使用 Rails 的内置内容。
在此 SO 帖子 ***.com/questions/7203304/… 中描述了您在 AJAX 中执行此操作的方式【参考方案4】:
从 Rails 5 开始,您还可以使用 ::API 而不是 ::Base: 创建一个新类:
class ApiController < ActionController::API
end
【讨论】:
【参考方案5】:如果您使用的是 Devise,请注意
对于 Rails 5,
protect_from_forgery
不再附加到before_action
链中,因此如果您在protect_from_forgery
之前设置了authenticate_user
,您的请求将导致“无法验证 CSRF 令牌真实性”。要解决此问题,请更改您调用它们的顺序,或使用protect_from_forgery prepend: true
。
Documentation
【讨论】:
【参考方案6】:如果您只想为一个或多个控制器操作(而不是整个控制器)跳过 CSRF 保护,试试这个
skip_before_action :verify_authenticity_token, only [:webhook, :index, :create]
[:webhook, :index, :create]
将跳过对这 3 个操作的检查,但您可以更改为任何您想跳过的操作
【讨论】:
【参考方案7】:如果要排除示例控制器的示例操作
class TestController < ApplicationController
protect_from_forgery except: :sample
def sample
render json: @hogehoge
end
end
您可以毫无问题地处理来自外部的请求。
【讨论】:
【参考方案8】:在 Rails 6 中,我找到了解决“无法验证 CSRF 令牌真实性”的简单方法。只是放
config.action_controller.default_protect_from_forgery = false # unless ENV["RAILS_ENV"] == "production"
在 application.rb 中
它在开发模式下很方便。但不要在生产中使用它。
【讨论】:
【参考方案9】:这个问题最简单的解决方法是在你的控制器中做标准的事情,或者你可以直接把它放到ApplicationController
:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception, prepend: true
end
【讨论】:
以上是关于Rails:发出 POST 请求时无法验证 CSRF 令牌的真实性的主要内容,如果未能解决你的问题,请参考以下文章
Rails API 422 无法处理的实体:没有可用的验证密钥,heroku
向 Fastify 发出 POST 请求时,JSON 未被解析以进行验证
当请求来自用户登录的页面时,自动验证DRF。如果外部发出api请求,则请求令牌验证