Rails: Não é possível verificar a autenticidade do token CSRF ao fazer uma solicitação POST

91

Eu quero fazer POST requestpara meu desenvolvedor local, assim:

  HTTParty.post('http://localhost:3000/fetch_heroku',
                :body => {:type => 'product'},)

No entanto, a partir do console do servidor, ele relata

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

Aqui está meu controlador e configuração de rotas, é bastante simples.

  def fetch_heroku
    if params[:type] == 'product'
      flash[:alert] = 'Fetch Product From Heroku'
      Heroku.get_product
    end
  end

  post 'fetch_heroku' => 'admin#fetch_heroku'

Não tenho certeza do que preciso fazer? Desligar o CSRF certamente funcionaria, mas acho que deveria ser meu erro ao criar tal API.

Há alguma outra configuração que preciso fazer?

cqcn1991
fonte
5
Para APIs é geralmente aceito desligar a validação de token CSRF . Eu uso protect_from_forgery with: :null_session.
dcestari

Respostas:

110

Falsificação de solicitação entre sites (CSRF / XSRF) ocorre quando uma página da web mal-intencionada engana os usuários para que executem uma solicitação que não se destina, por exemplo, ao uso de bookmarklets, iframes ou apenas criando uma página visualmente semelhante o suficiente para enganar os usuários.

A proteção Rails CSRF é feita para aplicativos da web "clássicos" - ela simplesmente oferece um grau de garantia de que a solicitação originou-se de seu próprio aplicativo da web. Um token CSRF funciona como um segredo que apenas o seu servidor conhece - o Rails gera um token aleatório e o armazena na sessão. Seus formulários enviam o token através de uma entrada oculta e o Rails verifica se qualquer solicitação não GET inclui um token que corresponde ao que está armazenado na sessão.

No entanto, uma API é geralmente, por definição, entre sites e deve ser usada em mais do que seu aplicativo da web, o que significa que todo o conceito de CSRF não se aplica.

Em vez disso, você deve usar uma estratégia baseada em token de autenticação de solicitações de API com uma chave e segredo de API, já que está verificando se a solicitação vem de um cliente de API aprovado - não de seu próprio aplicativo.

Você pode desativar o CSRF conforme apontado por @dcestari:

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

Atualizada. No Rails 5 você pode gerar aplicativos somente API usando a --apiopção:

rails new appname --api

Eles não incluem o middleware CSRF e muitos outros componentes que são superflouus.

max
fonte
Obrigado, eu escolho desativar parte do CSRF: stackoverflow.com/questions/5669322/…
cqcn1991
4
isso sugere que todas as APIs atendem ao tráfego de aplicativo para aplicativo (no qual um único parceiro recebe uma chave e um segredo exclusivos). Nesse cenário, como uma comunicação de servidor para servidor, essa resposta é apropriada. NO ENTANTO, o que é confuso para a maioria dos desenvolvedores de aplicativos da web é que, para um cliente Javascript, controlado e escrito por você, você NÃO QUER usar uma única chave secreta (isso exporia uma única chave secreta para todos os clientes). Em vez disso, o CSRF e o mecanismo de sessão de cookie do Rail funcionam muito bem - mesmo para aplicativos Javascript que usam sua API - se você passar o token CSRF de volta para o Rails com cada solicitação.
Jason FB de
a maneira como você faz isso em AJAX é descrita neste post do SO stackoverflow.com/questions/7203304/…
Jason FB
@JasonFB você está correto ao dizer que um único segredo com clientes javascript não funciona. No entanto, usar sessões e a proteção Rails CSRF ainda é problemático se você pretende construir uma API que também pode ser usada por outros tipos de clientes que não são navegadores.
máx.
Se você fornecer ou construir uma alternativa ao CSRF, fique à vontade. Basta saber o motivo do que você está substituindo para saber o que é apropriado para substituí-lo. Obviamente, agora nosso problema se tornou contextual.
Jason FB
79

Outra maneira de desligar o CSRF que não renderizará uma sessão nula é adicionar:

skip_before_action :verify_authenticity_token

em seu controlador Rails. Isso garantirá que você ainda tenha acesso às informações da sessão.

Novamente, certifique-se de fazer isso apenas em controladores de API ou em outros lugares onde a proteção CSRF não se aplica.

Matt Waldron
fonte
1
Isso é o mesmo que protect_from_forgery except: [:my_method_name]?
Arnold Roa
19

Há informações relevantes sobre a configuração do CSRF em relação aos controladores de API em api.rubyonrails.org :

É importante lembrar que as solicitações XML ou JSON também são afetadas e se você estiver criando uma API, deve alterar o método de proteção contra falsificação em ApplicationController(por padrão :exception:):

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

Podemos querer desabilitar a proteção CSRF para APIs, uma vez que normalmente são projetadas para não ter estado. Ou seja, o cliente da API de solicitação cuidará da sessão para você, em vez do Rails.

Sr. Tao
fonte
4
Isso é tão confuso. Estou oscilando entre fontes que dizem que protect_from_forgeryainda é necessário para APIs e fontes que dizem que não é. E se a API for para um aplicativo de página única, que usa o cookie de sessão para autenticação do usuário?
Wylliam Judd
O mecanismo CSRF é a maneira embutida do Rails para lidar com o vetor de ataque usando cookies de sessão. O mecanismo protege seus controladores, enquanto opera junto (na verdade dentro) do cookie de sessão Rails. Sem protect_from_forgery, ou desligando ou configurando um exceto nele, você está dizendo ao Rails para NÃO proteger aquela ação usando as informações do token CSRF (que é obtido do cookie de sessão).
Jason FB de
5
Acho que o que é confuso é que, para a documentação do Rails, "API" significa um aplicativo servidor a servidor que receberá solicitações de API de parceiros remotos e confiáveis ​​(nem todos os usuários da web que visitam seu site). Para aqueles ppl, forneça pares de chave-segredo exclusivos por meio de um mecanismo seguro não hackável. Para aplicativos da web modernos, onde muitas pessoas carregam seu aplicativo da web por meio de um cliente da web, você ainda usa uma sessão ou mecanismo baseado em token para identificar exclusivamente cada pessoa que visita seu site. Então, a menos que você esteja usando ALGUM OUTRO mecanismo para fazer isso (Json Web tokens, etc), fique com o material embutido do Rails.
Jason FB de
1
a maneira como você faz isso em AJAX é descrita neste post do SO stackoverflow.com/questions/7203304/…
Jason FB
13

Desde o Rails 5 você também pode criar uma nova classe com :: API em vez de :: Base:

class ApiController < ActionController::API
end
webaholik
fonte
3

Se você deseja excluir a ação de amostra do controlador de amostra

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

  def sample
     render json: @hogehoge
  end
end

Você pode processar solicitações de fora sem problemas.

Ryosuke Hujisawa
fonte
0

A solução mais simples para o problema é fazer coisas padrão em seu controlador ou você pode colocá-lo diretamente em ApplicationController

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

Arish Khan
fonte
0

Se você deseja apenas pular a proteção CSRF para uma ou mais ações do controlador (em vez de todo o controlador), tente isto

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

Onde [:webhook, :index, :create]pulará a verificação dessas 3 ações, mas você pode mudar para a que deseja pular

Stevec
fonte