Jeton d'authenticité Rails 4

198

Je travaillais sur une nouvelle application Rails 4 (sur Ruby 2.0.0-p0) lorsque j'ai rencontré des problèmes de jeton d'authenticité.

Lors de l'écriture d'un contrôleur qui répond à json (en utilisant la respond_tométhode de classe), je suis arrivé à l' createaction que j'ai commencé à obtenir des ActionController::InvalidAuthenticityTokenexceptions lorsque j'ai essayé de créer un enregistrement en utilisant curl.

J'ai fait en sorte de définir -H "Content-Type: application/json"et j'ai défini les données avec -d "<my data here>"mais toujours pas de chance.

J'ai essayé d'écrire le même contrôleur en utilisant Rails 3.2 (sur Ruby 1.9.3) et je n'ai eu aucun problème de jeton d'authenticité. J'ai cherché autour et j'ai vu qu'il y avait des changements avec les jetons d'authenticité dans Rails 4. D'après ce que je comprends, ils ne sont plus automatiquement insérés dans les formulaires? Je suppose que cela affecte en quelque sorte les types de contenu non HTML.

Existe-t-il un moyen de contourner cela sans avoir à demander un formulaire HTML, à arracher le jeton d'authenticité, puis à faire une autre demande avec ce jeton? Ou est-ce que je manque complètement quelque chose de complètement évident?

Edit: J'ai juste essayé de créer un nouvel enregistrement dans une nouvelle application Rails 4 en utilisant un échafaudage sans rien changer et je rencontre le même problème, donc je suppose que ce n'est pas quelque chose que j'ai fait.

alexcoco
la source

Réponses:

277

Je pense que je viens de comprendre. J'ai changé la (nouvelle) valeur par défaut

protect_from_forgery with: :exception

à

protect_from_forgery with: :null_session

selon le commentaire dans ApplicationController.

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

Vous pouvez voir la différence en regardant la source request_forgery_protecton.rbou, plus précisément, les lignes suivantes:

Dans 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

Dans Rails 4 :

def handle_unverified_request
  forgery_protection_strategy.new(self).handle_unverified_request
end

Qui appellera ce qui suit :

def handle_unverified_request
  raise ActionController::InvalidAuthenticityToken
end
alexcoco
la source
20
Vous pouvez supprimer l'option: avec tous ensemble,: null_session est la valeur par défaut: api.rubyonrails.org/classes/ActionController/…
Casey
6
Existe-t-il un moyen d'utiliser l'exception pour les appels non JSON et la session nulle pour les appels JSON (également appelés appels API)?
James McMahon
@JamesMcMahon Vous pourrez peut-être écrire le vôtre before_actionqui vérifie le format et si la demande est vérifiée. Je ne connais aucun moyen intégré de fixer des conditions.
alexcoco
1
Dans les rails 4.1.6, je devais également spécifier skip_before_action :verify_authenticity_tokensur le contrôleur d'application de mon API pour que cela fonctionne.
Waseem
1
Vous ne devriez plus avoir à désactiver :verify_authenticy_tokendans Rails 4.2. Il est par défaut :null_session, ce qui, comme son nom l'indique, vous donne simplement une demande sans session. Vous pouvez plutôt vérifier la demande via une clé API.
lobati
72

Au lieu de désactiver la protection csrf, il est préférable d'ajouter la ligne de code suivante dans le formulaire

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

et si vous utilisez form_for ou form_tag pour générer le formulaire, il ajoutera automatiquement la ligne de code ci-dessus dans le formulaire

xiaoboa
la source
9
C'est un bon conseil si vous créez un formulaire, mais la question fait référence aux appels d'API à l'aide de JSON.
James McMahon
1
Merci cependant, cela a répondu à une question que j'avais sur l'écriture manuelle d'un code lors de l'utilisation du guidon!
David Routen
70

L'ajout de la ligne suivante dans le formulaire a fonctionné pour moi:

<%= hidden_field_tag :authenticity_token, form_authenticity_token %>
Carlos
la source
12
ne s'applique pas vraiment aux appels api
courtsimas
2
Dans mon cas, j'utilise Rails4. Je peux soumettre le formulaire en cliquant sur le bouton soumettre. Mais si je soumets le formulaire via le code JS, cette erreur se produit. Et cette réponse a résolu le problème pour moi.
Chris.Zou
2
Pour mon application basée sur Rails 4.0.8, il suffisait d'écrire =token_tag nilou (en .erb)<%= token_tag nil %>
jmarceli
1
Vous semblez désactiver l'authentification en définissant le jeton sur zéro?
Carlos
est-il sûr de le faire?
Angel Garcia
33

Je ne pense pas qu'il soit bon de désactiver généralement la protection CSRF tant que vous n'implémentez pas exclusivement une API.

En consultant la documentation de l'API Rails 4 pour ActionController, j'ai constaté que vous pouvez désactiver la protection contre la falsification par contrôleur ou par méthode.

Par exemple, pour désactiver la protection CSRF pour les méthodes que vous pouvez utiliser

class FooController < ApplicationController
  protect_from_forgery except: :index
roamingthings
la source
Cela l'a fait pour moi dans Rails 4 - une erreur a été lancée après avoir tenté de supprimer. ^^
zero_cool
9

Je suis tombé sur le même problème. Corrigé en ajoutant à mon contrôleur:

      skip_before_filter :verify_authenticity_token, if: :json_request?
user1756254
la source
méthode non définie `json_request? ' pour # <SettingsController: 0x000000030a54a8>, une idée?
Deano
Vous devez y définir une méthode comme: def json_request? request.format.json? fin
Jai Kumar Rajput
7

As-tu essayé?

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

Ces fonctionnalités ont été ajoutées à des fins de sécurité et de protection contre la contrefaçon.
Cependant, pour répondre à votre question, voici quelques entrées. Vous pouvez ajouter ces lignes après votre nom de contrôleur.

Ainsi,

class NameController < ApplicationController
    skip_before_action :verify_authenticity_token

Voici quelques lignes pour différentes versions de rails.

Rails 3

skip_before_filter: verify_authenticity_token

Rails 4 :

skip_before_action: verify_authenticity_token


Si vous avez l'intention de désactiver cette fonction de sécurité pour toutes les routines de contrôleur, vous pouvez remplacer la valeur de protect_from_forgery par : null_session dans votre fichier application_controller.rb.

Ainsi,

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

Si vous utilisez jQuery avec des rails, méfiez-vous de permettre l'entrée aux méthodes sans vérifier le jeton d'authenticité.

jquery-ujs peut gérer les jetons pour vous

Vous devriez déjà l'avoir dans le joyau jquery-rails, mais vous devrez peut-être l'inclure dans application.js avec

//= require jquery_ujs

C'est tout ce dont vous avez besoin - votre appel ajax devrait maintenant fonctionner

Pour plus d'informations, voir: https://github.com/rails/jquery-ujs

user1555400
la source
1

Ajouter authenticity_token: trueà la balise de formulaire

Salma Gomaa
la source
0

Lorsque vous définissez votre propre formulaire html, vous devez inclure une chaîne de jeton d'authentification, qui doit être envoyée au contrôleur pour des raisons de sécurité. Si vous utilisez l'aide de formulaire rails pour générer le jeton d'authenticité est ajouté au formulaire comme suit.

<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>

La solution au problème consiste donc à ajouter le champ authenticity_token ou à utiliser des aides de forme de rails plutôt que de compromettre la sécurité, etc.

amjad
la source
1
Merci pour votre réponse, bien que cela ne réponde pas à la question d'origine. La question portait sur la réponse aux demandes JSON mais vous fournissez une solution pour un formulaire HTML à partir de zéro. Lorsque vous faites des demandes JSON (pensez à l'API), vous ne soumettez pas de formulaire HTML et vous n'avez peut-être pas facilement accès au jeton d'authenticité.
alexcoco
sauf si le contenu de la demande est en fait JSON, auquel cas vous souhaitez le définir application/json.
alexcoco
Je comprends que la réponse n'est pas exactement ce que vous recherchez. Mais le but de mettre une réponse connexe est d'aider les utilisateurs à la recherche de "problèmes d'authenticité" similaires.
amjad
désolé, j'ai supprimé par erreur - "vous pouvez définir Content-Type: application / x-www-form-urlencoded avec la solution ci-dessus".
amjad
0

Tous mes tests fonctionnaient bien. Mais pour une raison quelconque, j'avais défini ma variable d'environnement sur non-test:

export RAILS_ENV=something_non_test

J'ai oublié de désactiver cette variable à cause de laquelle j'ai commencé à recevoir une ActionController::InvalidAuthenticityTokenexception.

Après désarmement $RAILS_ENV, mes tests ont recommencé à fonctionner.

mridula
la source