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

时间:2016-02-03 15:40:17

标签: ruby-on-rails

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

我还需要其他任何设置吗?

8 个答案:

答案 0 :(得分:75)

跨站点请求伪造(CSRF / XSRF)是指恶意网页欺骗用户执行请求,例如通过使用bookmarklet,iframe或仅通过创建视觉上类似于欺骗用户的页面。

Rails CSRF protection是针对" classic"网络应用程序 - 它只是给出了一定程度的保证,即请求来自您自己的网络应用程序。 CSRF令牌就像只有您的服务器知道的秘密一样--Rails生成随机令牌并将其存储在会话中。您的表单通过隐藏的输入发送令牌,Rails验证任何非GET请求是否包含与会话中存储的令牌匹配的令牌。

然而,API通常是定义跨站点,并且意味着不仅仅用于您的Web应用程序,这意味着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中间件和许多其他超流程组件。

答案 1 :(得分:44)

关闭不会呈现空会话的CSRF的另一种方法是添加:

skip_before_action :verify_authenticity_token
在您的Rails控制器中

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

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

答案 2 :(得分:10)

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

  

     

记住XML或JSON请求也会受到影响非常重要,如果您要构建API,则应更改:exception中的伪造保护方法(默认情况下:{ {1}}):

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

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

     

答案 3 :(得分:5)

在Rails 5中,您还可以使用::API代替:: Base创建新类:

class ApiController < ActionController::API
end

答案 4 :(得分:0)

如果要排除样本控制器的样本操作

class TestController < ApplicationController
  protect_from_forgery :except => [:sample]

  def sample
     render json: @hogehoge
  end
end

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

答案 5 :(得分:0)

最简单的解决方案是在控制器中执行标准操作,也可以直接将其放入 ApplicationController

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

答案 6 :(得分:0)

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

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

[:webhook, :index, :create]将跳过对这3个动作的检查,但是您可以更改为要跳过的任何内容

答案 7 :(得分:0)

如果您使用的是 Devise,请注意

<块引用>

对于 Rails 5,protect_from_forgery 不再附加到 before_action 链,因此如果您在 authenticate_user 之前设置了 protect_from_forgery,您的请求将导致“Can” t 验证 CSRF 令牌的真实性。”要解决此问题,请更改您调用它们的顺序,或使用 protect_from_forgery prepend: true

Documentation