Je travaillais sur une nouvelle application Rails 4 (sur Ruby 2.0.0-p0) lorsque j'ai rencontré un problème de jeton d'authenticité.
Lors de l'écriture d'un contrôleur qui répond à json (à l'aide de la méthode respond_to
class), je suis arrivé à l'action create
J'ai commencé à obtenir des exceptions ActionController::InvalidAuthenticityToken
lorsque j'ai tenté de créer un enregistrement à l'aide de curl
.
Je me suis assuré de régler -H "Content-Type: application/json"
et 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 aucun problème de jeton d'authenticité. J'ai cherché autour et j'ai vu qu'il y avait quelques 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 concerne en quelque sorte les types de contenu non HTML.
Existe-t-il un moyen de résoudre ce problème sans avoir à demander un formulaire HTML, en saisissant le jeton d'authenticité, puis en faisant une autre demande avec ce jeton? Ou est-ce que je manque complètement quelque chose qui est complètement évident?
Edit: Je viens d'essayer 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, alors je suppose que ce n'est pas fait.
Je pense que je viens de le comprendre. J'ai changé le (nouveau) 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 de request_forgery_protecton.rb
, ou 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 le suivant :
def handle_unverified_request
raise ActionController::InvalidAuthenticityToken
end
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.
L'ajout de la ligne suivante dans le formulaire a fonctionné pour moi:
<%= hidden_field_tag :authenticity_token, form_authenticity_token %>
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.
Lors de l'examen de la documentation de l'API Rails 4 pour ActionController , j'ai constaté que vous pouviez désactiver la protection contre la falsification sur un contrôleur ou une base de méthodes.
Par exemple, pour désactiver la protection CSRF des méthodes que vous pouvez utiliser
class FooController < ApplicationController
protect_from_forgery except: :index
Je suis tombé sur le même problème. Corrigé en ajoutant à mon contrôleur:
skip_before_filter :verify_authenticity_token, if: :json_request?
As-tu essayé?
protect_from_forgery with: :null_session, if: Proc.new {|c| c.request.format.json? }
Ce document officiel - explique comment désactiver correctement la protection contre la falsification de l’API http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html
Ceci est une fonctionnalité de sécurité dans Rails. Ajoutez cette ligne de code sous la forme:
<%= hidden_field_tag :authenticity_token, form_authenticity_token %>
La documentation peut être trouvée ici: http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html
Ces fonctionnalités ont été ajoutées à des fins de sécurité et de protection contre la falsification.
Cependant, pour répondre à votre question, voici quelques entrées. Vous pouvez ajouter ces lignes après le nom du contrôleur.
Ainsi,
class NameController < ApplicationController
skip_before_action :verify_authenticity_token
Voici quelques lignes pour différentes versions de Rails.
Rails
skip_before_filter: verify_authenticity_token
Rails 4:
skip_before_action: verify_authenticity_token
Si vous souhaitez désactiver cette fonctionnalité de sécurité pour toutes les routines de contrôleur, vous pouvez modifier la valeur de protect_from_forgery en : null_session sur votre fichier application_controller.rb.
Ainsi,
class ApplicationController < ActionController::Base
protect_from_forgery with: :null_session
end
Ajouter authenticity_token: true
à la balise de formulaire
Si vous utilisez jQuery avec Rails, veillez à ne pas autoriser la saisie de 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 gem 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
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ésélectionner cette variable à cause de laquelle j'ai commencé à obtenir une exception ActionController::InvalidAuthenticityToken
.
Après avoir désactivé $Rails_ENV
, mes tests ont recommencé à fonctionner.
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 Rails l'assistant de formulaire 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="✓" />
<input name="authenticity_token" type="hidden" value="x37DrAAwyIIb7s+w2+AdoCR8cAJIpQhIetKRrPgG5VA=">
</div>
...
</form>
La solution au problème consiste donc à ajouter un champ authenticity_token ou à utiliser Rails form helpers plutôt que de compromettre la sécurité, etc.