Token de Autenticidade do Rails 4

198

Eu estava trabalhando em um novo aplicativo Rails 4 (no Ruby 2.0.0-p0) quando me deparei com alguns problemas de token de autenticidade.

Ao escrever um controlador que responde ao json (usando o respond_tométodo de classe), cheguei à createação que comecei a receber ActionController::InvalidAuthenticityTokenexceções quando tentei criar um registro usando curl.

Eu fiz questão de definir -H "Content-Type: application/json"e definir os dados com, -d "<my data here>"mas ainda sem sorte.

Tentei escrever o mesmo controlador usando o Rails 3.2 (no Ruby 1.9.3) e não tive nenhum problema de token de autenticidade. Pesquisei e vi que havia algumas alterações nos tokens de autenticidade no Rails 4. Pelo que entendi, eles não são mais inseridos automaticamente nos formulários? Suponho que isso esteja afetando de alguma forma os tipos de conteúdo não HTML.

Existe alguma maneira de contornar isso sem precisar solicitar um formulário HTML, capturando o token de autenticidade e fazendo outra solicitação com esse token? Ou estou perdendo completamente algo que é completamente óbvio?

Edit: Eu apenas tentei criar um novo registro em um novo aplicativo Rails 4 usando um andaime sem alterar nada e estou tendo o mesmo problema, então acho que não foi algo que fiz.

alexcoco
fonte

Respostas:

277

Eu acho que acabei de descobrir. Eu mudei o (novo) padrão

protect_from_forgery with: :exception

para

protect_from_forgery with: :null_session

conforme o comentário em ApplicationController.

# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.

Você pode ver a diferença consultando a fonte request_forgery_protecton.rbou, mais especificamente, as seguintes linhas:

No Rails 3.2 :

# This is the method that defines the application behavior when a request is found to be unverified.
# By default, \Rails resets the session when it finds an unverified request.
def handle_unverified_request
  reset_session
end

No Rails 4 :

def handle_unverified_request
  forgery_protection_strategy.new(self).handle_unverified_request
end

O que chamará o seguinte :

def handle_unverified_request
  raise ActionController::InvalidAuthenticityToken
end
alexcoco
fonte
20
Você pode remover a opção: with all together,: null_session é o padrão: api.rubyonrails.org/classes/ActionController/…
Casey
6
Existe uma maneira de usar exceção para chamadas não JSON e sessão nula para chamadas JSON (também chamadas de API)?
James McMahon
@JamesMcMahon Você pode escrever o seu próprio before_actionque verifica o formato e se a solicitação foi verificada. Não conheço nenhuma forma construída para definir condições.
alexcoco
1
No rails 4.1.6, eu tive que especificar também skip_before_action :verify_authenticity_tokenno controlador de aplicativos da minha API para fazer isso funcionar.
Waseem
1
Você não precisa :verify_authenticy_tokenmais desativar o Rails 4.2. O padrão é :null_sessionque, como o nome indica, apenas fornece uma solicitação sem uma sessão. Você pode verificar a solicitação por meio de uma chave de API.
lobati
72

Em vez de desativar a proteção csrf, é melhor adicionar a seguinte linha de código no formulário

<%= tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token) %> 

e se você estiver usando form_for ou form_tag para gerar o formulário, ele adicionará automaticamente a linha de código acima no formulário

xiaoboa
fonte
9
Esse é um bom conselho se você estiver criando um formulário, mas a pergunta está se referindo a fazer chamadas de API usando JSON.
James McMahon
1
Obrigado, porém, isso respondeu a uma pergunta que eu tinha sobre escrever um código manualmente enquanto usava o guidão!
precisa
70

Adicionar a seguinte linha ao formulário funcionou para mim:

<%= hidden_field_tag :authenticity_token, form_authenticity_token %>
Carlos
fonte
12
realmente não se aplica a chamadas de API
courtsimas
2
No meu caso, estou usando o Rails4. Para enviar o formulário, clique no botão Enviar. Mas se eu enviar o formulário via código JS, esse erro ocorre. E esta resposta resolveu o problema para mim.
Chris.Zou
2
Para o meu Rails 4.0.8 App base foi o suficiente para escrever =token_tag nilou (em .erb)<%= token_tag nil %>
jmarceli
1
Você parece estar desativando a autenticação configurando o token para zero?
Carlos
é seguro fazer isso?
Angel Garcia
33

Não acho que seja bom desativar a proteção contra CSRF, desde que você não implemente exclusivamente uma API.

Ao examinar a documentação da API do Rails 4 para o ActionController , descobri que você pode desativar a proteção contra falsificação em uma base por controlador ou por método.

Por exemplo, para desativar a proteção CSRF para métodos que você pode usar

class FooController < ApplicationController
  protect_from_forgery except: :index
roamingthings
fonte
Isso foi feito por mim no Rails 4 - erro ao ser lançado após tentar excluir. ^^
zero_cool
9

Me deparei com o mesmo problema. Corrigido adicionando ao meu controlador:

      skip_before_filter :verify_authenticity_token, if: :json_request?
user1756254
fonte
método indefinido `json_request? ' para # <Controlador de configurações: 0x000000030a54a8>, Alguma idéia?
Deano 23/03
Você tem que definir um método lá como: def json_request? request.format.json? fim
Jai Kumar Rajput
7

Você tentou?

 protect_from_forgery with: :null_session, if: Proc.new {|c| c.request.format.json? }
zechtz
fonte
2

Esses recursos foram adicionados para fins de segurança e proteção contra falsificação.
No entanto, para responder à sua pergunta, aqui estão algumas entradas. Você pode adicionar essas linhas após o nome do controlador.

Igual a,

class NameController < ApplicationController
    skip_before_action :verify_authenticity_token

Aqui estão algumas linhas para diferentes versões de trilhos.

Trilhos 3

skip_before_filter: confirm_authenticity_token

Trilhos 4 :

skip_before_action: confirm_authenticity_token


Caso pretenda desativar esse recurso de segurança para todas as rotinas do controlador, você pode alterar o valor de protect_from_forgery para : null_session no arquivo application_controller.rb.

Igual a,

class ApplicationController < ActionController::Base
  protect_from_forgery with: :null_session
end
Kent Aguilar
fonte
1

Se você estiver usando o jQuery com trilhos, tenha cuidado ao permitir a entrada de métodos sem verificar o token de autenticidade.

jquery-ujs pode gerenciar os tokens para você

Você já deve tê-lo como parte da gema jquery-rails, mas pode ser necessário incluí-lo no application.js com

//= require jquery_ujs

É tudo o que você precisa - sua chamada ajax agora deve funcionar

Para mais informações, consulte: https://github.com/rails/jquery-ujs

user1555400
fonte
1

Adicionar authenticity_token: trueà tag do formulário

Salma Gomaa
fonte
0

Ao definir seu próprio formulário html, é necessário incluir a sequência do token de autenticação, que deve ser enviada ao controlador por motivos de segurança. Se você usar o auxiliar de formulário do rails para gerar o token de autenticidade, ele será adicionado ao formulário da seguinte forma.

<form accept-charset="UTF-8" action="/login/signin" method="post">
  <div style="display:none">
    <input name="utf8" type="hidden" value="&#x2713;" />
    <input name="authenticity_token" type="hidden" value="x37DrAAwyIIb7s+w2+AdoCR8cAJIpQhIetKRrPgG5VA=">
  </div>
    ...
</form>

Portanto, a solução para o problema é adicionar o campo authenticity_token ou usar auxiliares de formulário de trilhos, em vez de comprometer a segurança etc.

amjad
fonte
1
Obrigado pela sua resposta, embora isso não responda à pergunta original. A pergunta feita sobre a resposta às solicitações JSON, mas você está fornecendo uma solução para um formulário HTML do zero. Ao fazer solicitações JSON (think API), você não está enviando um formulário HTML e pode não ter acesso fácil ao token de autenticidade.
Alexcoco
a menos que o conteúdo da solicitação seja realmente JSON, nesse caso, você deseja configurá-lo application/json.
Alexcoco 06/07
Entendo que a resposta não é exatamente o que você está procurando. Mas o objetivo de colocar respostas relacionadas é ajudar os usuários a procurar por "Problemas de autenticidade" semelhantes.
6114
desculpe, removi por engano - "você pode definir o Tipo de conteúdo: application / x-www-form-urlencoded com a solução acima".
6134
0

Todos os meus testes estavam funcionando bem. Mas, por algum motivo, eu havia definido minha variável de ambiente como não teste:

export RAILS_ENV=something_non_test

Esqueci de desmarcar essa variável por causa da qual comecei a receber ActionController::InvalidAuthenticityTokenexceção.

Após desarmar $RAILS_ENV, meus testes começaram a funcionar novamente.

mridula
fonte