Rails: 在发出 POST 请求时无法验证 CSRF 令牌的真实性

我想使 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 应该是我的错误。

还有什么我需要做的吗?

176417 次浏览

跨站点请求伪造(CSRF/XSRF)是指一个恶意网页欺骗用户执行一个并非有意的请求,例如通过使用 bookmarklet、 iframe 或者仅仅通过创建一个在视觉上足够相似以欺骗用户的页面。

Rails CSRF 保护是为“经典”网络应用程序而设计的——它只是给出了一定程度的保证,即请求来自你自己的网络应用程序。CSRF 令牌的工作原理类似于只有服务器知道的秘密—— Rails 生成一个随机令牌并将其存储在会话中。表单通过隐藏输入发送令牌,Rails 验证任何非 GET 请求是否包含与会话中存储的内容匹配的令牌。

然而,在一个打算跨站点使用,甚至服务于非浏览器客户端的 API 中,由于跨域 cookie 和提供 CSRF 令牌的问题,它并不是很有用。

In that case you should use a token based strategy of authenticating API requests with an API key and secret since you are verifying that the request comes from an approved API client - not from your own app.

正如@dcestari 指出的那样,你可以停用 CSRF:

class ApiController < ActionController::Base
protect_from_forgery with: :null_session
end

更新。 在 Rails 5中,你可以通过使用 --api选项只生成 API 应用程序:

rails new appname --api

它们不包括 CSRF 中间件和许多其他冗余的组件。

Another way to turn off CSRF that won't render a null session is to add:

skip_before_action :verify_authenticity_token

这将确保您仍然可以访问会话信息。

同样,请确保只在 API 控制器或 CSRF 保护不太适用的其他地方执行此操作。

Api.rubyonrails.org上有关于 CSRF 相对于 API 控制器的配置的相关信息:

Something

请记住,XML 或 JSON 请求也会受到影响,如果您正在构建 空气污染指数,那么您应该更改 ApplicationController中的伪造保护方法(默认值: :exception) :

class ApplicationController < ActionController::Base
protect_from_forgery unless: -> { request.format.json? }
end

我们可能希望禁用 API 的 CSRF 保护,因为它们通常被设计为无状态的。也就是说,请求 空气污染指数客户机将为您而不是 Rails 处理会话。

Something

由于 Rails 5,您还可以用 ::API代替: : Base 创建一个新类:

class ApiController < ActionController::API
end

如果要排除示例控制器的示例操作

class TestController < ApplicationController
protect_from_forgery except: :sample


def sample
   render json: @hogehoge
end
end

您可以毫无问题地处理来自外部的请求。

这个问题最简单的解决方案是在你的控制器中做一些标准的事情,或者你可以直接把它放到 ApplicationController中:

class ApplicationController < ActionController::Base
protect_from_forgery with: :exception, prepend: true
end

如果只想跳过一个或多个控制器操作(而不是整个控制器)的 CSRF 保护,请尝试这样做

skip_before_action :verify_authenticity_token, only [:webhook, :index, :create]

Where [:webhook, :index, :create] will skip the check for those 3 actions, but you can change to whichever you want to skip

如果你在使用 Devise 请注意

对于 Rails 5,protect_from_forgery不再预先设置在 before_action链中,因此如果您在 protect_from_forgery之前设置了 authenticate_user,那么您的请求将导致“无法验证 CSRF 令牌的真实性”要解决这个问题,可以更改调用它们的顺序,或者使用 protect_from_forgery prepend: true

文件

在 Rails 6中,我找到了一个简单的方法来解决这个“无法验证 CSRF 令牌的真实性”

config.action_controller.default_protect_from_forgery = false # unless ENV["RAILS_ENV"] == "production"

在应用。 rb

它在开发模式上很方便,但在生产中不能使用。