Je rencontre des problèmes concernant le jeton d'authenticité dans Rails, comme je l'ai souvent fait maintenant.
Mais je ne veux vraiment pas simplement résoudre ce problème et continuer. J'aimerais vraiment comprendre le jeton Authenticité. Ma question est la suivante: avez-vous une source complète d’informations sur ce sujet ou passeriez-vous votre temps à expliquer en détail ici?
Que se passe-t-il
Lorsque l'utilisateur visualise un formulaire pour créer, mettre à jour ou détruire une ressource, l'application Rails crée un authenticity_token
aléatoire, stocke ce jeton dans la session et le place dans un champ masqué de la fenêtre. forme. Lorsque l'utilisateur soumet le formulaire, Rails cherche le authenticity_token
, le compare à celui stocké dans la session et, s'ils correspondent, la demande est autorisée à continuer.
Pourquoi cela se produit
Comme le jeton d'authenticité est stocké dans la session, le client ne peut pas connaître sa valeur. Cela empêche les utilisateurs de soumettre des formulaires à une application Rails sans consulter le formulaire au sein de cette application. Imaginez que vous utilisez le service A, vous vous êtes connecté au service et tout va bien. Maintenant, imaginez que vous utilisiez le service B, que vous voyiez une photo qui vous plaisait et que vous appuyiez sur la photo pour l'agrandir. Maintenant, si du code malveillant existait 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 (falsification de requête intersite) .
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 serait pas autorisée à continuer.
API docs décrit les détails de la balise META:
La protection CSRF est activée avec la méthode
protect_from_forgery
, 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_token
par défaut. Le nom et la valeur de ce jeton doivent être ajoutés à chaque mise en forme générant des formulaires en incluantcsrf_meta_tags
dans l'en-tête HTML.
Notes
Gardez à l'esprit que Rails ne vérifie que les méthodes non idempotentes (POST, PUT/PATCH et DELETE). Les requêtes GET ne sont pas vérifiées pour le jeton d'authenticité. Pourquoi? parce que la spécification HTTP indique que les requêtes GET sont idempotentes et qu'elles ne devraient pas créer, modifier ou détruire des ressources sur le serveur, et la requête devrait être idempotente (si vous exécutez la même commande plusieurs fois, vous devriez obtenir le même résultat à chaque fois).
De plus, la véritable implémentation est un peu plus compliquée que celle définie 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 non plus et ne stocke pas 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 à ceux stockés, chaque fois qu'une page est rendue. Voir request_forgery_protection.rb .
Leçons
Utilisez authenticity_token
pour protéger vos méthodes non idempotentes (POST, PUT/PATCH et DELETE). Veillez également à ne pas autoriser les requêtes GET susceptibles de modifier les ressources du serveur.
EDIT: Vérifiez le commentaire de @erturne concernant les demandes GET étant idempotentes. Il l'explique mieux que moi.
Le jeton d'authenticité est conçu pour que vous sachiez que votre formulaire est soumis à partir de votre site Web. Il est généré à partir de la machine sur laquelle il s'exécute avec un identifiant unique que seul votre ordinateur peut connaître, évitant ainsi les attaques par falsification de requêtes entre sites.
Si vous rencontrez simplement des difficultés avec Rails refusant votre accès au script AJAX, vous pouvez utiliser
<%= form_authenticity_token %>
générer le bon jeton lors de la création de votre formulaire.
Vous pouvez en savoir plus à ce sujet dans la documentation .
Le jeton d'authenticité est une contre-mesure à la falsification de demande intersite (CSRF). Qu'est-ce que CSRF, demandez-vous?
C'est une façon pour un attaquant de potentiellement pirater des sessions sans même connaître les jetons de session.
scénario:
solution CSRF:
Exemple d'attaque minimale à éviter: CSRF
Sur mon site internet evil.com
je vous convaincs 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 par le biais de cookies de session, ceux-ci seraient envoyés et le transfert serait effectué sans même que vous le sachiez.
C'est là que le jeton CSRF entre en jeu:
Ainsi, le formulaire sur un navigateur authentique ressemblerait à ceci:
<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 paramètre authenticity_token
, et je ne pouvais pas le deviner, car il s'agit d'un nombre aléatoire énorme.
Cette technique de prévention s'appelle Modèle de jeton de synchroniseur .
Politique de même origine
Mais que se passe-t-il si l'attaquant fait deux requêtes avec JavaScript, une pour lire le jeton et l'autre pour effectuer le transfert?
Le modèle de jeton de synchronisation ne suffit pas à empêcher cela!
C’est là que la politique de la même origine s’applique à la rescousse, comme je l’ai expliqué à: https://security.stackexchange.com/questions/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 comme form_tag
ajoutent un champ caché au formulaire si ce n'est pas un formulaire GET
AJAX est traité automatiquement par jquery-ujs , qui lit le jeton à partir des éléments meta
ajoutés à votre en-tête par csrf_meta_tags
(présent dans le modèle par défaut), et l'ajoute. à toute demande faite.
uJS tente également de mettre à jour le jeton dans des formulaires contenant des fragments mis en cache obsolètes.
Autres approches de prévention
X-Requested-With
: Origin
: https://security.stackexchange.com/questions/91165/why-is-the-synchronizer-token-pattern-preferred-over-the-Origin-) header-check-toLa méthode Authenticity Token
est la méthode de Rails consistant à prevent'attaques par falsification de requêtes intersites (CSRF ou XSRF)' .
En termes simples, il s'assure que les requêtes PUT/POST/DELETE (méthodes pouvant modifier le contenu) envoyées à votre application Web sont effectuées à partir du navigateur du client et non d'un tiers (un attaquant) qui: a accès à un cookie créé côté client.
Le jeton d'authenticité est utilisé pour empêcher les attaques de type CSRF (Cross-Site Request Forgery). Pour comprendre le jeton d'authenticité, vous devez d'abord comprendre les attaques CSRF.
Supposons que vous soyez l'auteur de _bank.com
_. Sur votre site, vous avez un formulaire utilisé pour transférer de l'argent sur un compte différent avec une demande GET:
Un pirate informatique pourrait simplement envoyer une requête HTTP au serveur en indiquant _GET /transfer?amount=$1000000&account-to=999999
_, non?
Faux. L'attaque des hackers ne fonctionnera pas. Le serveur va fondamentalement penser?
Hein? Qui est ce gars qui essaie d'initier 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 cookie _session_id
_ authentifiant le demandeur.
Lorsque vous vous connectez avec votre nom d'utilisateur et votre mot de passe, le serveur définit un cookie _session_id
_ 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 cookie _session_id
_, le serveur sait:
Oh, c'est John Doe. Il s'est connecté avec succès il y a 2,5 minutes. Il est bon d'y aller.
Un pirate informatique pourrait penser:
Hmm. Une requête HTTP normale ne fonctionnera pas, mais si je pouvais mettre la main sur ce cookie _
session_id
_, je serais en or.
Le navigateur de l'utilisateur contient de nombreux cookies pour le domaine _bank.com
_. Tous les cookies sont envoyés chaque fois que l'utilisateur adresse une demande au domaine _bank.com
_. Y compris le cookie _session_id
_.
Donc, si un pirate pouvait obtenir vous de faire la demande GET qui transfère de l'argent sur son compte, il aurait gain de cause. Comment pourrait-il vous inciter à le faire? Avec falsification de demande intersite.
C'est assez simple, en fait. Le pirate informatique pourrait 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 de l'utilisateur rencontre cette balise d'image, il envoie une requête GET à cette URL. Et puisque la demande provient de son navigateur, il enverra tous les cookies associés à _bank.com
_. Si l'utilisateur s'est récemment connecté à _bank.com
_, le cookie _session_id
_ sera défini et le serveur pensera que l'utilisateur souhaitait transférer 1 000 000 USD sur le compte 999999!
Eh bien, ne visitez pas de sites dangereux et tout ira bien.
Cela ne suffit pas. Et si quelqu'un publiait cette image sur Facebook et que celle-ci apparaisse sur votre mur? Que se passe-t-il s'il est injecté dans un site visité par 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é de manière dynamique. Voici l'exemple de 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>
_
Quand votre ApplicationController
a ceci:
_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="✓" />
<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 étant sûre.
Comment un attaquant est-il supposé savoir ce qu'est ce jeton? Une valeur différente est générée aléatoirement à chaque génération du formulaire:
Une attaque XSS (Cross Site Scripting) - voilà comment. Mais c'est une vulnérabilité différente pour un autre jour.
puisque Authenticity Token
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
Attention, le mécanisme de jeton d'authenticité peut entraîner des conditions de concurrence si vous avez plusieurs demandes simultanées du même client. Dans cette situation, votre serveur peut générer plusieurs jetons d'authenticité lorsqu'il ne devrait y en avoir qu'un, et le client recevant le jeton précédent dans un formulaire échouera lors de la prochaine requête car le jeton de cookie de session a été écrasé. Il existe une description de ce problème et une solution pas tout à fait triviale ici: http://www.paulbutcher.com/2007/05/race-conditions-in-Rails-sessions-and-how-tow-to-) corrigez-les /
Méthodes où authenticity_token
est requis
authenticity_token
est requis dans le cas de méthodes idempotentes telles que post, put et delete, car les méthodes Idempotent affectent les données.
Pourquoi est-il nécessaire
Il est nécessaire d'empêcher les mauvaises actions. authenticity_token est stocké en 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 le formulaire sur le serveur. Avant d'exécuter l'action, authenticity_token envoyé par l'utilisateur est vérifié avec
authenticity_token
enregistré dans la session. Siauthenticity_token
est identique, le processus est poursuivi, sinon il ne réalise aucune action.
Qu'est-ce qu'un authentication_token?
Il s'agit d'une chaîne aléatoire utilisée par l'application Rails pour s'assurer que l'utilisateur demande ou exécute une action à partir de la page de l'application, et non d'une autre application ou site.
Pourquoi un authentication_token est-il nécessaire?
Pour protéger votre application ou votre site contre la falsification de requêtes entre sites.
Comment ajouter un authentication_token à un formulaire?
Si vous générez un formulaire en utilisant form_for tag, un authentication_token est automatiquement ajouté, sinon vous pouvez utiliser <%= csrf_meta_tag %>
.