Des Rails, de la conception de l'authentification, CSRF problème
Je suis en train de faire un singe-la page de l'application à l'aide de Rails. Lors de la signature dans et hors de Concevoir des contrôleurs sont appelés à l'aide d'ajax. Le problème que je reçois est que quand j'1) connectez-vous 2) déconnecter, puis de signer à nouveau ne fonctionne pas.
Je pense que c'est lié à CSRF token qui est remis quand j'ai sign out (bien qu'il ne devrait pas autant que je sache) et depuis elle est seule page, l'ancien jeton CSRF est envoyé dans xhr demande donc de réinitialiser la session.
Être plus concret c'est le flux de travail:
- Signe dans
- Signe
- Signe de la réussite 201. Cependant imprime
WARNING: Can't verify CSRF token authenticity
dans les journaux de serveur) - Ultérieure requête ajax échoue 401 non autorisé
- Actualiser le site web (à ce stade, CSRF dans l'en-tête de page des modifications à autre chose)
- Je peux vous connecter, il fonctionne, jusqu'à ce que j'essaie de vous déconnecter et de se reconnecter.
Des indices très apprécié! Laissez-moi savoir si je peux ajouter plus de détails.
- Puis-je vous demander si vous pouvez répondre à cette question très semblable? stackoverflow.com/questions/50159847/...
Vous devez vous connecter pour publier un commentaire.
Jimbo a fait un travail merveilleux en expliquant le "pourquoi" derrière le problème que vous rencontrez. Il existe deux approches que vous pouvez prendre pour résoudre le problème:
(Tel que recommandé par Jimbo) Remplacer Concevoir::SessionsController pour revenir à la nouvelle csrf token:
Et de créer un gestionnaire de succès pour votre sign_out demande sur le côté client (probablement besoin de quelques ajustements en fonction de votre configuration, par exemple, OBTENIR vs SUPPRIMER):
Cela suppose également que vous êtes, y compris le jeton CSRF automatiquement avec toutes les requêtes AJAX avec quelque chose comme ceci:
Beaucoup plus simplement, si c'est approprié pour votre application, vous pouvez simplement remplacer le
Devise::SessionsController
et remplacer le jeton de vérifier avecskip_before_filter :verify_authenticity_token
.:create
action. J'ai fait une question à ce sujet ici (stackoverflow.com/questions/26640326/...) et voudrais vraiment l'apprécier si vous avez un moment pour le vérifierJe viens de lancer dans ce type de problème. Il y a beaucoup de choses ici.
TL;DR - La raison de l'échec est que le jeton CSRF est associé à votre session sur le serveur (vous avez un serveur de session si vous êtes connecté ou déconnecté). Le jeton CSRF est inclus dans les DOM votre page à chaque chargement de page. Lors de la déconnexion de votre session est remise à zéro et n'a pas de jeton csrf. Normalement, une déconnexion redirige vers une autre page/action, ce qui vous donne un nouveau jeton CSRF, mais puisque vous êtes à l'aide d'ajax, vous devez le faire manuellement.
$('meta[name="csrf-token"]').attr('content', <NEW_CSRF_TOKEN>)
Explication plus Détaillée Vous avez probablement obtenu
protect_from_forgery
défini dans votre ApplicationController.rb fichier à partir duquel tous les autres contrôleurs de l'héritage (ce qui est assez commun je crois).protect_from_forgery
effectue CSRF vérifie tous les non-GET HTML/Javascript demandes. Depuis Concevoir de Connexion est un POST, il effectue une CSRF Vérifier. Si une CSRF Vérification échoue, alors l'utilisateur de la session en cours est effacé, c'est à dire, les journaux de l'utilisateur, parce que le serveur suppose que c'est une attaque (qui est la bonne/comportement souhaité).Donc, en supposant que vous êtes débutant dans un déconnecté de l'état, vous faites un frais de chargement de la page, et de ne jamais recharger à nouveau à la page:
Sur le rendu de la page: le serveur insère le Jeton CSRF associé à votre serveur de session dans la page. Vous pouvez consulter ce jeton en exécutant la commande suivante à partir d'une console javascript dans votre navigateur
$('meta[name="csrf-token"]').attr('content')
.Vous puis connectez-vous via un XMLHttpRequest: Votre Jeton CSRF reste inchangée à ce point pour que le Jeton CSRF dans votre Session est toujours conforme à celle qui a été inséré dans la page. Derrière les coulisses, sur le côté client, jquery-ujs est à l'écoute pour xhr et la fixation d'un "X-CSRF Token' en-tête avec la valeur de
$('meta[name="csrf-token"]').attr('content')
automatiquement pour vous (rappelez-vous c'était le Jeton CSRF défini à l'étape 1 par le serveur). Le serveur compare le Jeton dans l'en-tête par jquery-ujs et celui qui est stocké dans votre session d'information et qu'ils correspondent à la demande réussit.Vous pouvez alors vous Connecter à l'aide d'un XMLHttpRequest: Cela réinitialise session, vous donne une nouvelle session sans un Jeton CSRF.
Vous puis connectez-vous à nouveau par le biais d'un XMLHttpRequest: jquery-ujs tire le jeton CSRF de la valeur de
$('meta[name="csrf-token"]').attr('content')
. Cette valeur est encore votre VIEUX jeton CSRF. Il prend ce vieux jeton et l'utilise pour définir le "X-CSRF Token'. Le serveur compare cette valeur d'en-tête avec un nouveau jeton CSRF qu'il ajoute à votre session, ce qui est différent. Cette différence provoque leprotect_form_forgery
à l'échec, qui jette leWARNING: Can't verify CSRF token authenticity
et réinitialise votre session, qui enregistre l'utilisateur.Vous puis faire un autre XMLHttpRequest qui nécessite que l'utilisateur connecté: La session en cours n'ont pas un utilisateur connecté afin de concevoir renvoie un 401.
Mise à jour: 8/14 Concevoir de déconnexion ne vous donne pas un nouveau jeton CSRF, la redirection qui se passe normalement après une déconnexion vous donne un nouveau jeton csrf.
params[:authenticity_token]
est le même lors de la demande xhr pour se connecter et le html #créer une demande. Toute l'aide ici??Devise::SessionsController
de retour de nouveau le courant csrf de jetons dans la:create
et:destroy
actions? Votre code client doit ensuite lire et définir ces nouvelles valeurs à partir de la réponse, et de les envoyer avec toutes les futures demandes. Si ce n'est pas clair, faites le moi savoir et je vais essayer de poster l'intégralité de notre actuellement de solution de travail plus tard (il peut me prendre un jour ou deux)..js.erb
modèle que la dernière étape de mon:create
action dans mon extension deSessionsController
. Si j'ajoute un autre rendu de retour JSON, je vais faire un double de rendu d'erreur. Puis-je inclure le courant de jetons csrf dans le modèle et de le lire encore et de les mettre en quelque sorte? J'ai créé ma propre question qui montre que le contrôleur de ici: stackoverflow.com/questions/26640326/....js.erb
fichier$('meta[name="csrf-token"]').attr('content', <%= new_csrf_token %>)
vous pouvez définir le nouveau jeton csrf directement. Ensuite dans votre site javascript au chargement de la page, cela$.ajaxPrefilter( function(options, originalOptions, xhr) { xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content');}
. C'est le dernier morceau de @jredburn de la première approche, qui envoie le jeton avec toutes les requêtes ajax.application.js
j'ai ajouté$(document).ajaxSend(function (e, xhr, options) { xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content')) });
et j'ai ajouté la ligne à mon.js.erb
fichier, mais maintenant la requête Ajax de signer en obtient un500
erreur carnew_csrf_token
est pas défini. Je suppose que je suis censé mettre quelque chose d'autre là-bas, mais quoi?form_authenticity_token
dans mon controller, puis ajouté les deux lignes suivantes à la.js.erb
modèle de rendu:$('meta[name="csrf-token"]').attr('content', '<%= session["_csrf_token"] %>') $('input[name="authenticity_token"]').val('<%= session["_csrf_token"] %>')
new_csrf_token
était juste un espace réservé pour une variable qui contient les utilisateurs du nouveau jeton csrf, alors, oui,form_authenticity_token
de sens. Ces deux lignes de bien paraître.$(document).ajaxSend
ligne que vous avez ajouté à votre application.js. Vous devez vous assurer que la ligne se fait exécuter au chargement de la page.Ma réponse s'inspire fortement de deux @Jimbo et @Sija, cependant, je suis en utilisant le dispositif/angularjs convention a été suggéré à Rails de Protection CSRF + Angular.js: protect_from_forgery me fait déconnecter sur le POST, et d'élaborer un peu sur mon blog quand j'ai d'abord fait ce. C'est une méthode sur le contrôleur de l'application pour définir des cookies pour csrf:
Donc je suis en utilisant @Sija du format, mais en utilisant le code de ce plus tôt, AFIN de solution, ce qui me donne:
Pour être complet, car il m'a fallu quelques minutes pour s'en sortir, je note également la nécessité de modifier votre fichier config/routes.rb de déclarer que vous avez remplacé les séances de contrôleur. Quelque chose comme:
C'était une partie d'un grand CSRF nettoyage que j'ai fait sur mon application, ce qui peut être intéressant pour les autres. Le blog est ici, les autres changements comprennent:
Sauvetage de ActionController::InvalidAuthenticityToken, ce qui signifie que si les choses se désynchroniser l'application va se fixer, plutôt que l'utilisateur n'ait à effacer les cookies. En l'état des choses dans les rails je pense que votre contrôleur d'application sera par défaut avec:
Dans cette situation, vous devez alors:
J'ai aussi eu la douleur avec des conditions de course et certaines interactions avec le timeoutable module à Concevoir, que j'ai commenté plus loin dans le billet de blog - en bref, vous devez envisager d'utiliser le active_record_store plutôt que cookie_store, et être prudent quant à l'émission de demandes parallèles à proximité de sign_in et sign_out actions.
C'est mon point de vue:
Et sur le côté client:
Qui va garder votre CSRF balises meta mis à jour chaque fois que vous retournez
X-CSRF-Token
ouX-CSRF-Param
d'en-tête via une requête ajax.request_forgery_protection_token
etform_authenticity_token
de mon contrôleur comme je l'ai été une erreur de ne pas pouvoir appelersplit
sur:authenticity_token:String
... Mais cette technique fonctionne vraiment bien. Merci.Unexpected error while processing request: undefined method
chaque " pour :authenticity_token:Symbole`Après avoir insisté sur le Gardien de la source, j'ai remarqué que la mise
sign_out_all_scopes
àfalse
arrêts de Gardien de l'effacement de l'ensemble de la session, de sorte que le jeton CSRF est conservé entre signe les aboutissants.Discussion en Concevoir question à détente: https://github.com/plataformatec/devise/issues/2200
J'ai juste ajouté ceci dans mon fichier de mise en page et il a travaillé
De vérifier si vous avez inclus dans votre application.js fichier
La raison en est, jquery-gem rails qui règle automatiquement le jeton CSRF sur toutes les requêtes Ajax par défaut, les besoins de ces deux
Dans mon cas, après la connexion de l'utilisateur, j'ai besoin de redessiner l'utilisateur menu. Cela a fonctionné, mais j'ai eu CSRF authenticité des erreurs sur chaque requête au serveur, dans ce même article (sans actualisation de la page, bien sûr). Solutions ci-dessus ne fonctionnait pas comme je voulais rendre un js vue.
Ce que j'ai fait est-ce, à l'aide de Concevoir:
app/controllers/sessions_controller.rb
Après que j'ai fait une demande au contrôleur#action qui redessinent le menu. Et dans le javascript, j'ai modifié le X-CSRF-Param et X-CSRF Token:
app/views/utilitaires/redraw_user_menu.js.erb
J'espère que c'est utile pour quelqu'un sur le même js situation 🙂
Ma situation était encore plus simple. Dans mon cas, tout ce que je voulais faire, c'était ceci: si une personne est assise sur un écran avec un formulaire, et de leur session (Concevoir timeoutable d'expiration de la session), normalement, si ils ont frappé Soumettre à ce point, Concevoir permettrait de renvoyer dos à l'écran de connexion. Eh bien, je ne veux pas que, parce qu'ils perdent toutes leurs données de formulaire. J'utilise JavaScript pour attraper le formulaire de soumission, appel Ajax un contrôleur qui détermine si l'utilisateur n'est plus connecté, et si c'est le cas, j'ai mis en place un formulaire où ils retaper son mot de passe, et je authentifier leur (bypass_sign_in dans un contrôleur) à l'aide d'un appel Ajax. Le formulaire de soumission est autorisé à continuer.
Fonctionnait parfaitement jusqu'à ce que j'ai ajouté protect_from_forgery.
Alors, merci pour les réponses ci-dessus, tout ce que j'avais était vraiment dans mon contrôleur, où je signe à l'utilisateur de retourner dans (la bypass_sign_in) je viens de mettre une variable d'instance pour le nouveau jeton CSRF:
et puis dans le .js.erb qui a été rendu (car encore une fois, c'était un appel XHR):
Le tour est joué. Ma page de formulaire, qui n'a pas été actualisée et, par conséquent, a été coincé avec l'ancien jeton, a maintenant un nouveau jeton de la nouvelle session que j'ai eu de la signature de mon utilisateur.
en réponse à un commentaire de @sixty4bit; si vous rencontrez cette erreur:
remplacer
avec