Comprendre le jeton d'authenticité Rails

983

Je rencontre des problèmes concernant le jeton d'authenticité dans les rails, comme je l'ai maintenant plusieurs fois.

Mais je ne veux vraiment pas simplement résoudre ce problème et continuer. J'aimerais vraiment comprendre le jeton Authenticité. Eh bien, ma question est, avez-vous une source complète d'informations à ce sujet ou passeriez-vous votre temps à expliquer en détail ici?

Ricardo Acras
la source
7
Voir également: "Pourquoi Google ajoute-t-il un délai (1) à sa réponse JSON?" stackoverflow.com/questions/2669690/…
Chloe

Réponses:

1463

Ce qui se produit

Lorsque l'utilisateur affiche un formulaire pour créer, mettre à jour ou détruire une ressource, l'application Rails crée un aléatoire authenticity_token, stocke ce jeton dans la session et le place dans un champ masqué du formulaire. Lorsque l'utilisateur soumet le formulaire, Rails recherche le authenticity_token, le compare à celui stocké dans la session et s'il correspond à la demande, il est autorisé à continuer.

Pourquoi ça arrive

Étant donné que le jeton d'authenticité est stocké dans la session, le client ne peut pas connaître sa valeur. Cela empêche les gens de soumettre des formulaires à une application Rails sans afficher le formulaire dans cette application elle-même. Imaginez que vous utilisez le service A, vous vous êtes connecté au service et tout va bien. Imaginez maintenant que vous êtes allé utiliser le service B, que vous avez vu une image que vous aimez et que vous avez appuyé sur l'image pour l'agrandir. Maintenant, si un code malveillant était présent au service B, il pourrait envoyer une demande au service A (auquel vous êtes connecté) et demander de supprimer votre compte en envoyant une demande à http://serviceA.com/close_account. C'est ce qu'on appelle CSRF (Cross Site Request Forgery) .

Si le service A utilise des jetons d'authenticité, ce vecteur d'attaque n'est plus applicable, car la demande du service B ne contiendrait pas le jeton d'authenticité correct et ne sera pas autorisée à continuer.

La documentation de l'API décrit les détails de la balise META:

La protection CSRF est activée avec la protect_from_forgeryméthode, qui vérifie le jeton et réinitialise la session si elle ne correspond pas à ce qui était attendu. Un appel à cette méthode est généré par défaut pour les nouvelles applications Rails. Le paramètre de jeton est nommé authenticity_tokenpar défaut. Le nom et la valeur de ce jeton doivent être ajoutés à chaque mise en page qui rend des formulaires en les incluant csrf_meta_tagsdans l'en-tête HTML.

Remarques

Gardez à l'esprit que Rails vérifie uniquement les méthodes non idempotentes (POST, PUT / PATCH et DELETE). Le jeton d'authenticité des requêtes GET n'est pas vérifié. Pourquoi? car la spécification HTTP stipule que les requêtes GET sont idempotentes et ne doivent pas créer, modifier ou détruire des ressources sur le serveur, et la requête doit être idempotente (si vous exécutez la même commande plusieurs fois, vous devriez obtenir le même résultat à chaque fois).

De plus, l'implémentation réelle est un peu plus compliquée comme défini au début, assurant une meilleure sécurité. Rails n'émet pas le même jeton stocké avec chaque formulaire. Il ne génère pas et ne stocke pas non plus un jeton différent à chaque fois. Il génère et stocke un hachage cryptographique dans une session et émet de nouveaux jetons cryptographiques, qui peuvent être comparés à celui stocké, chaque fois qu'une page est rendue. Voir request_forgery_protection.rb .

Cours

Utilisez authenticity_tokenpour protéger vos méthodes non idempotentes (POST, PUT / PATCH et DELETE). Assurez-vous également de ne pas autoriser les demandes GET qui pourraient potentiellement modifier les ressources sur le serveur.


EDIT: Vérifiez le commentaire de @erturne concernant les demandes GET idempotentes. Il l'explique mieux que moi ici.

Faisal
la source
25
@Faisal, est-il alors possible, pour un attaquant, de simplement lire / capturer l'élément «caché» du formulaire pour le service A et d'obtenir ce jeton unique généré pour l'utilisateur - étant donné qu'il a obtenu l'accès à la session démarrée par l'utilisateur pour le service A?
marcamillion
11
@marcamillion: Si quelqu'un a détourné votre session au service A, le jeton d'authenticité ne vous protégera pas. Le pirate de l'air pourra soumettre une demande et il sera autorisé à procéder.
Faisal
12
@zabba: Rails déclenche une exception ActionController :: InvalidAuthenticityToken si un formulaire est soumis sans le jeton approprié. Vous pouvez sauver_de l'exception et effectuer le traitement de votre choix.
Faisal
5
re "Assurez-vous également de ne faire aucune demande GET qui pourrait potentiellement modifier les ressources sur le serveur." - cela inclut de ne pas utiliser match () dans les routes qui pourraient potentiellement permettre des demandes GET aux actions du contrôleur destinées à recevoir uniquement des POST
Steven Soroka
102
"... et la demande doit être idempotente (si vous exécutez la même commande plusieurs fois, vous devriez obtenir le même résultat à chaque fois)." Juste une clarification subtile ici. Sûr signifie pas d'effets secondaires. Idempotent signifie le même effet secondaire, peu importe combien de fois un service est appelé. Tous les services sûrs sont intrinsèquement idempotents car il n'y a pas d'effets secondaires. Appeler plusieurs fois GET sur une ressource d'instant présent renverrait un résultat différent à chaque fois, mais c'est sûr (et donc idempotent).
erturne
137

Le jeton d'authenticité est conçu pour que vous sachiez que votre formulaire est envoyé depuis votre site Web. Il est généré à partir de la machine sur laquelle il s'exécute avec un identifiant unique que seule votre machine peut connaître, ce qui aide à prévenir les attaques de contrefaçon de requêtes intersites.

Si vous rencontrez simplement des difficultés avec les rails refusant l'accès à votre script AJAX, vous pouvez utiliser

<%= form_authenticity_token %>

pour générer le jeton correct lorsque vous créez votre formulaire.

Vous pouvez en savoir plus à ce sujet dans la documentation .

Topher Fangio
la source
88

Qu'est-ce que le CSRF?

Le jeton d'authenticité est une contre-mesure à la contrefaçon de demande intersite (CSRF). Qu'est-ce que le CSRF, demandez-vous?

C'est un moyen pour un attaquant de détourner potentiellement des sessions sans même connaître les jetons de session.

Scénario :

  • Visitez le site de votre banque, connectez-vous.
  • Ensuite, visitez le site de l'attaquant (par exemple, annonce sponsorisée d'une organisation non fiable).
  • La page de l'attaquant comprend un formulaire avec les mêmes champs que le formulaire "Transfert de fonds" de la banque.
  • L'attaquant connaît les informations de votre compte et possède des champs de formulaire pré-remplis pour transférer de l'argent de votre compte vers le compte de l'attaquant.
  • La page de l'attaquant inclut Javascript qui soumet le formulaire à votre banque.
  • Lorsque le formulaire est envoyé, le navigateur inclut vos cookies pour le site bancaire, y compris le jeton de session.
  • La banque transfère de l'argent sur le compte de l'attaquant.
  • Le formulaire peut être dans une iframe invisible, de sorte que vous ne savez jamais que l'attaque s'est produite.
  • Il s'agit de la contrefaçon de demande intersite (CSRF).

Solution CSRF :

  • Le serveur peut marquer les formulaires provenant du serveur lui-même
  • Chaque formulaire doit contenir un jeton d'authentification supplémentaire en tant que champ masqué.
  • Le jeton doit être imprévisible (l'attaquant ne peut pas le deviner).
  • Le serveur fournit un jeton valide dans les formulaires de ses pages.
  • Le serveur vérifie le jeton lorsque le formulaire est publié, rejette les formulaires sans le jeton approprié.
  • Exemple de jeton: identifiant de session chiffré avec la clé secrète du serveur.
  • Rails génère automatiquement de tels jetons: consultez le champ de saisie authenticity_token dans chaque formulaire.
Rose Perrone
la source
1
Voici une version de cette même explication moins précise mais aussi moins abstraite: stackoverflow.com/a/33829607/2810305
Lutz Prechelt
Je ne suis pas sûr, mais les navigateurs modernes permettent-ils d'envoyer des requêtes non idempotentes (POST / PUT / DELETE) vers un autre domaine? Je suppose qu'il doit y avoir une protection contre de telles choses dans le navigateur lui
divideByZero
45

Exemple d'attaque minimale qui serait évitée: CSRF

Sur mon site Internet, evil.comje vous convainc de soumettre le formulaire suivant:

<form action="http://bank.com/transfer" method="post">
  <p><input type="hidden" name="to"      value="ciro"></p>
  <p><input type="hidden" name="ammount" value="100"></p>
  <p><button type="submit">CLICK TO GET PRIZE!!!</button></p>
</form>

Si vous êtes connecté à votre banque via des cookies de session, les cookies seront envoyés et le transfert sera effectué sans que vous le sachiez.

C'est là que le jeton CSRF entre en jeu:

  • avec la réponse GET qui a renvoyé le formulaire, Rails envoie un très long paramètre caché aléatoire
  • lorsque le navigateur fait la requête POST, il envoie le paramètre et le serveur ne l'accepte que s'il correspond

Ainsi, le formulaire sur un navigateur authentique ressemblerait à:

<form action="http://bank.com/transfer" method="post">
  <p><input type="hidden" name="authenticity_token" value="j/DcoJ2VZvr7vdf8CHKsvjdlDbmiizaOb5B8DMALg6s=" ></p>
  <p><input type="hidden" name="to"                 value="ciro"></p>
  <p><input type="hidden" name="ammount"            value="100"></p>
  <p><button type="submit">Send 100$ to Ciro.</button></p>
</form>

Ainsi, mon attaque échouerait, car elle n'envoyait pas le authenticity_tokenparamètre, et il n'y a aucun moyen que j'aurais pu le deviner car c'est un nombre aléatoire énorme.

Cette technique de prévention est appelée Synchronizer Token Pattern .

Politique de même origine

Mais que se passe-t-il si l'attaquant a fait deux requêtes avec JavaScript, une pour lire le jeton et la seconde pour effectuer le transfert?

Le modèle de jeton de synchroniseur ne suffit pas à lui seul pour empêcher cela!

C'est là que la même politique d'origine vient à la rescousse, comme je l'ai expliqué à: /security/8264/why-is-the-same-origin-policy-so-important/72569# 72569

Comment Rails envoie les jetons

Couvert à: Rails: Comment fonctionne csrf_meta_tag?

Fondamentalement:

  • Les assistants HTML aiment form_tagajouter un champ caché au formulaire pour vous s'il ne s'agit pas d'un formulaire GET

  • AJAX est traité automatiquement par jquery-ujs , qui lit le jeton des metaéléments ajoutés à votre en-tête par csrf_meta_tags(présent dans le modèle par défaut) et l'ajoute à toute demande effectuée.

    uJS essaie également de mettre à jour le jeton dans des formulaires dans des fragments mis en cache obsolètes.

Autres approches de prévention

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
la source
Merci, mais votre point sur le fait de s'appuyer sur la même politique d'origine pour ne pas être en mesure de lire le jeton CSRF en premier semble défectueux. Donc, d'abord, vous dites que vous pouvez POSTER vers une origine différente mais ne pouvez pas lire à partir de cela, cela semble bizarre, mais je suppose que c'est correct, mais vous pouvez injecter une image ou une balise de script avec un accès à la page et lier un gestionnaire pour analyser la réponse et l'obtenir oui?
bjm88
@ bjm88 injecter le script où? Sur votre site ou sur le site attaqué? Si le site est attaqué, autoriser l'injection de script est une faille de sécurité bien connue et met en gage efficacement le site Web. Chaque site Web doit le combattre par l'assainissement des intrants. Pour les images, je ne vois pas comment elles peuvent être utilisées pour une attaque. Sur le site attaquant: vous pouvez modifier votre navigateur pour autoriser la lecture, et donc vous mettre en gage automatiquement à volonté :-) mais des navigateurs décents l'empêchent par défaut, essayez-le.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
43

Le jeton d'authenticité est utilisé pour empêcher les attaques de type Cross-Site Request Forgery (CSRF). Pour comprendre le jeton d'authenticité, vous devez d'abord comprendre les attaques CSRF.

CSRF

Supposons que vous êtes l'auteur de bank.com. Vous avez un formulaire sur votre site qui est utilisé pour transférer de l'argent vers un autre compte avec une demande GET:

entrez la description de l'image ici

Un pirate pourrait simplement envoyer une requête HTTP au serveur en disant GET /transfer?amount=$1000000&account-to=999999, non?

entrez la description de l'image ici

Faux. L'attaque des pirates ne fonctionnera pas. Le serveur va penser essentiellement?

Hein? Qui est ce type qui essaie de lancer un transfert. Ce n'est pas le propriétaire du compte, c'est sûr.

Comment le serveur sait-il cela? Parce qu'il n'y a pas de session_idcookie authentifiant le demandeur.

Lorsque vous vous connectez avec votre nom d'utilisateur et votre mot de passe, le serveur place un session_idcookie sur votre navigateur. De cette façon, vous n'avez pas à authentifier chaque demande avec votre nom d'utilisateur et votre mot de passe. Lorsque votre navigateur envoie le session_idcookie, le serveur sait:

Oh, c'est John Doe. Il s'est connecté avec succès il y a 2,5 minutes. Il est prêt à partir.

Un pirate pourrait penser:

Hmm. Une requête HTTP normale ne fonctionnera pas, mais si je pouvais mettre la main sur ce session_idcookie, je serais en or.

Le navigateur des utilisateurs dispose d'un ensemble de cookies pour le bank.comdomaine. Chaque fois que l'utilisateur fait une demande au bank.comdomaine, tous les cookies sont envoyés. Y compris le session_idcookie.

Donc, si un pirate pouvait vous demander de faire la demande GET qui transfère de l'argent sur son compte, il réussirait. Comment pourrait-il vous inciter à le faire? Avec la contrefaçon de demande intersite.

C'est assez simple, en fait. Le pirate pourrait simplement vous faire visiter son site Web. Sur son site Web, il pourrait avoir la balise d'image suivante:

<img src="http://bank.com/transfer?amount=$1000000&account-to=999999">

Lorsque le navigateur des utilisateurs rencontre cette balise d'image, il fera une demande GET à cette URL. Et puisque la demande provient de son navigateur, elle enverra avec elle tous les cookies associés bank.com. Si l'utilisateur s'était récemment connecté à bank.com... le session_idcookie sera installé, et le serveur pensera que l'utilisateur voulait transférer 1 000 000 $ sur le compte 999999!

entrez la description de l'image ici

Eh bien, ne visitez pas de sites dangereux et tout ira bien.

Ce n'est pas suffisant. Et si quelqu'un poste cette image sur Facebook et qu'elle apparaît sur votre mur? Que se passe-t-il s'il est injecté dans un site que vous visitez avec une attaque XSS?

Ce n'est pas si grave. Seules les demandes GET sont vulnérables.

Pas vrai. Un formulaire qui envoie une demande POST peut être généré dynamiquement. Voici l'exemple du guide Rails sur la sécurité :

<a href="http://www.harmless.com/" onclick="
  var f = document.createElement('form');
  f.style.display = 'none';
  this.parentNode.appendChild(f);
  f.method = 'POST';
  f.action = 'http://www.example.com/account/destroy';
  f.submit();
  return false;">To the harmless survey</a>

Jeton d'authenticité

Lorsque vous avez ApplicationControllerceci:

protect_from_forgery with: :exception

Cette:

<%= form_tag do %>
  Form contents
<% end %>

Est compilé dans ceci:

<form accept-charset="UTF-8" action="/" method="post">
  <input name="utf8" type="hidden" value="&#x2713;" />
  <input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9PgwJ108nDHX/8Cts=" />
  Form contents
</form>

En particulier, les éléments suivants sont générés:

<input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9PgwJ108nDHX/8Cts=" />

Pour se protéger contre les attaques CSRF, si Rails ne voit pas le jeton d'authenticité envoyé avec une demande, il ne considérera pas la demande comme sûre.

Comment un attaquant est-il censé savoir ce qu'est ce jeton? Une valeur différente est générée aléatoirement à chaque génération du formulaire:

entrez la description de l'image ici

Une attaque Cross Site Scripting (XSS) - c'est comme ça. Mais c'est une vulnérabilité différente pour un jour différent.

Adam Zerner
la source
39

La Authenticity Tokenméthode is rails pour empêcher les attaques de contrefaçon de requêtes intersites (CSRF ou XSRF) .

Pour faire simple, il s'assure que les requêtes PUT / POST / DELETE (méthodes pouvant modifier le contenu) de votre application web sont faites depuis le navigateur du client et non depuis un tiers (un attaquant) qui a accès à un cookie créé côté client.

et moi
la source
34

car Authenticity Tokenc'est si important, et dans Rails 3.0+ vous pouvez utiliser

 <%= token_tag nil %>

créer

<input name="authenticity_token" type="hidden" value="token_value">

nulle part

Yuan He
la source
Cela m'a été utile. J'essayais en fait de XSSle faire sur la page de connexion, pas à des fins néfastes, mais pour créer une nouvelle session avec un nom d'utilisateur pré-rempli. Maintenant, je sais que je peux simplement utiliser value="token_value".
Michael - Où est Clay Shirky le
27

Attention, le mécanisme du jeton d'authenticité peut entraîner des conditions de concurrence critique si vous avez plusieurs demandes simultanées du même client. Dans cette situation, votre serveur peut générer plusieurs jetons d'authenticité alors qu'il ne devrait y en avoir qu'un, et le client recevant le jeton précédent dans un formulaire échouera à sa prochaine demande car le jeton de cookie de session a été remplacé. Il y a un article sur ce problème et une solution pas tout à fait triviale ici: http://www.paulbutcher.com/2007/05/race-conditions-in-rails-sessions-and-how-to-fix-them/

jdp
la source
11

Méthodes Où authenticity_tokenest requis

authenticity_token est requis en cas de méthodes idempotentes telles que post, put et delete, car les méthodes idempotentes affectent les données.

Pourquoi est-il requis

Il est nécessaire pour empêcher les actions mauvaises. authenticity_token est stocké dans la session, chaque fois qu'un formulaire est créé sur des pages Web pour créer ou mettre à jour des ressources, un jeton d'authenticité est stocké dans un champ caché et envoyé avec un formulaire sur le serveur. Avant d'exécuter l'action, l'utilisateur envoyé authenticity_token est recoupé avec authenticity_tokenstocké dans la session. Si authenticity_tokenc'est la même chose, le processus se poursuit sinon il n'exécute aucune action.

uma
la source
3
En fait, n'est-ce pas le contraire? GET est idempotent car son appel ne doit pas modifier l'état du système, où les verbes PUT POST et DELETE ne sont PAS des verbes idempotents car ils modifient l'état du système. IE: authenticity_token est requis en cas de méthodes NON idempotentes.
Jean-Théo
2
@ Jean-Daube, uma: idempotent signifie que si c'est fait deux fois, l'action ne se produit qu'une seule fois. GET, PUT et DELETE sont idempotents: w3.org/Protocols/rfc2616/rfc2616-sec9.html La propriété clé ici n'est pas idempotence, mais si la méthode change ou non les données, appelées ou non "méthode sûre".
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
6

Qu'est-ce qu'un jeton d'authentification?

Il s'agit d'une chaîne aléatoire utilisée par l'application rails pour vous assurer que l'utilisateur demande ou exécute une action à partir de la page de l'application, pas à partir d'une autre application ou d'un site.

Pourquoi un jeton d'authentification est-il nécessaire?

Pour protéger votre application ou votre site contre la contrefaçon de demande intersite.

Comment ajouter un jeton d'authentification à un formulaire?

Si vous générez un formulaire à l'aide de la balise form_for, un jeton d'authentification est automatiquement ajouté, que vous pouvez utiliser <%= csrf_meta_tag %>.

Pradeep Sapkota
la source