web-dev-qa-db-fra.com

Qu'est-ce qu'un jeton CSRF? Quelle est son importance et comment ça marche?

J'écris une application (Django, ça se produit) et je veux juste une idée de ce qu'est en réalité un "jeton CSRF" et de la façon dont il protège les données. Les données de publication ne sont-elles pas sécurisées si vous n'utilisez pas de jetons CSRF?

543
Shawn

Falsification de requête intersite (CSRF) en mots simples

  • Supposons que vous êtes actuellement connecté à votre banque en ligne à l'adresse www.mybank.com
  • Supposons qu'un transfert d'argent de mybank.com entraînera une demande (conceptuelle) de la forme http://www.mybank.com/transfer?to=<SomeAccountnumber>;amount=<SomeAmount>. (Votre numéro de compte n'est pas nécessaire, car il est impliqué dans votre identifiant.)
  • Vous visitez www.cute-cat-pictures.org, sans savoir qu’il s’agit d’un site malveillant.
  • Si le propriétaire de ce site connaît le formulaire de la requête ci-dessus (facile!) Et devine correctement que vous êtes connecté à mybank.com (nécessite de la chance!), Il pourrait inclure sur leur page une requête du type http://www.mybank.com/transfer?to=123456;amount=10000. (où 123456 est le numéro de leur compte aux Iles Caïman et 10000 est un montant que vous pensiez précédemment être content de posséder).
  • Vous avez récupéré cette page www.cute-cat-pictures.org, de sorte que votre navigateur faire cette demande.
  • Votre banque ne peut pas reconnaître cette origine de la demande: votre navigateur Web enverra la demande avec votre cookie www.mybank.com et il semblera parfaitement légitime. Voilà votre argent!

C'est le monde sans jetons CSRF.

Maintenant pour le meilleur avec jetons CSRF:

  • La demande de transfert est étendue avec un troisième argument: http://www.mybank.com/transfer?to=123456;amount=10000;token=31415926535897932384626433832795028841971.
  • Ce jeton est un nombre aléatoire énorme, impossible à deviner, que mybank.com inclura sur sa propre page Web lorsqu’il vous le servira. C'est différent chaque fois qu'ils servent une page à qui que ce soit.
  • L’attaquant n’est pas en mesure de deviner le jeton, il n’est pas en mesure de convaincre votre navigateur Web de le rendre (si le navigateur fonctionne correctement ...), de sorte que l’attaquant ne le fera pas pouvoir créer une demande valide, car les demandes avec le jeton incorrect (ou sans jeton) seront refusées par www.mybank.com.

Résultat: vous conservez vos 10000 unités monétaires. Je suggère que vous en donniez une partie à Wikipedia.

(Votre kilométrage peut varier.)

EDIT d'un commentaire qui mérite d'être lu:

Il serait utile de noter que le script de www.cute-cat-pictures.org n'a normalement pas accès à votre jeton anti-CSRF de www.mybank.com en raison du contrôle d'accès HTTP. Cette note est importante pour certaines personnes qui envoient de manière déraisonnable un en-tête Access-Control-Allow-Origin: * pour chaque réponse de site Web sans savoir pourquoi, simplement parce qu'elles ne peuvent pas utiliser l'API d'un autre site Web.

1357
Lutz Prechelt

Oui, les données de publication sont sécurisées. Mais l'origine de ces données n'est pas. De cette façon, quelqu'un peut inciter l'utilisateur JS à se connecter à votre site tout en naviguant sur la page Web de l'attaquant.

Afin d'éviter cela, Django enverra une clé aléatoire à la fois dans les cookies et les données de formulaire. Ensuite, lorsque les utilisateurs postent, il vérifie si deux clés sont identiques. Dans le cas où l'utilisateur est trompé, un site Web tiers ne peut pas obtenir les cookies de votre site, ce qui provoque une erreur d'authentification.

216

Le site génère un jeton unique lorsqu'il crée la page de formulaire. Ce jeton est requis pour publier/récupérer des données sur le serveur.

Étant donné que le jeton est généré par votre site et fourni uniquement lorsque la page contenant le formulaire est générée, un autre site ne peut pas imiter vos formulaires. Ils n'auront pas le jeton et ne pourront donc pas publier sur votre site.

71
tkone

Le blog Cloud Under a une bonne explication des jetons CSRF.

Imaginez que vous ayez un site Web semblable à Twitter simplifié, hébergé sur a.com. Les utilisateurs connectés peuvent entrer du texte (un Tweet) dans un formulaire envoyé au serveur sous forme de demande POST et publié lorsqu'ils cliquent sur le bouton d'envoi. Sur le serveur, l'utilisateur est identifié par un cookie contenant son identifiant de session unique afin que votre serveur sache qui a posté le Tweet.

Le formulaire pourrait être aussi simple que cela:

 <form action="http://a.com/Tweet" method="POST">
   <input type="text" name="Tweet">
   <input type="submit">
 </form> 

Imaginons maintenant qu’un méchant copie et colle ce formulaire sur son site Web malveillant, disons b.com. Le formulaire fonctionnerait toujours. Tant qu'un utilisateur est connecté à votre Twitter (c'est-à-dire qu'il possède un cookie de session valide pour a.com), la demande POST est envoyée à http://a.com/Tweet et traitée normalement l'utilisateur clique sur le bouton d'envoi.

Jusqu'à présent, ce n'est pas un gros problème tant que l'utilisateur est informé de ce que fait exactement le formulaire, mais que se passe-t-il si notre méchant modifie le formulaire comme suit:

 <form action="https://example.com/Tweet" method="POST">
   <input type="hidden" name="Tweet" value="Buy great products at http://b.com/#iambad">
   <input type="submit" value="Click to win!">
 </form> 

Désormais, si l'un de vos utilisateurs se retrouve sur le site Web du méchant et clique sur le bouton "Cliquez pour gagner!", Le formulaire est envoyé sur votre site Web, l'utilisateur est correctement identifié par l'ID de session contenu dans le cookie et le Tweet caché devient publié.

Si notre méchant était encore pire, il ferait en sorte que l'utilisateur innocent soumette ce formulaire dès qu'il ouvrirait sa page Web à l'aide de JavaScript, peut-être même complètement caché dans un iframe invisible. Il s’agit essentiellement d’une falsification de requêtes intersites.

Un formulaire peut facilement être soumis de partout à partout. Généralement, c’est une caractéristique commune, mais il existe bien d’autres cas où il est important d’autoriser uniquement la soumission d’un formulaire à partir du domaine auquel il appartient.

La situation est encore pire si votre application Web ne fait pas la distinction entre les demandes POST et GET (par exemple, dans PHP en utilisant $ _REQUEST au lieu de $ _POST). Ne fais pas ça! Les demandes de modification de données pourraient être soumises aussi facilement que <img src="http://a.com/tweet?tweet=This+is+really+bad">, intégrées à un site Web malveillant ou même à un courrier électronique.

Comment puis-je m'assurer qu'un formulaire ne peut être soumis qu'à partir de mon propre site Web? C’est là que le jeton CSRF entre en jeu. Un jeton CSRF est une chaîne aléatoire, difficile à deviner. Sur une page contenant un formulaire que vous souhaitez protéger, le serveur génère une chaîne aléatoire, le jeton CSRF, l'ajoute au formulaire en tant que champ masqué et s'en souvient également, en le stockant dans la session ou en définissant un cookie. contenant la valeur. Maintenant, le formulaire ressemblerait à ceci:

    <form action="https://example.com/Tweet" method="POST">
      <input type="hidden" name="csrf-token" value="nc98P987bcpncYhoadjoiydc9ajDlcn">
      <input type="text" name="Tweet">
      <input type="submit">
    </form> 

Lorsque l'utilisateur soumet le formulaire, le serveur doit simplement comparer la valeur du champ envoyé csrf-token (le nom n'a pas d'importance) avec le jeton CSRF mémorisé par le serveur. Si les deux chaînes sont égales, le serveur peut continuer à traiter le formulaire. Sinon, le serveur doit immédiatement arrêter le traitement du formulaire et répondre avec une erreur.

Pourquoi ça marche? Il y a plusieurs raisons pour lesquelles le méchant de notre exemple ci-dessus est incapable d'obtenir le jeton CSRF:

Copier le code source statique de notre page vers un autre site Web serait inutile, car la valeur du champ caché change avec chaque utilisateur. Sans que le site Web du méchant connaisse le jeton CSRF de l'utilisateur actuel, votre serveur rejettera toujours la demande POST.

Parce que la page malveillante du méchant est chargée par le navigateur de votre utilisateur depuis un domaine différent (b.com au lieu de a.com), le méchant n'a aucune chance de coder un code JavaScript, qui charge le contenu et donc le jeton CSRF actuel de notre utilisateur depuis votre site web. En effet, les navigateurs Web n'autorisent pas les demandes interdomaine AJAX par défaut.

Le méchant ne peut pas non plus accéder au cookie défini par votre serveur, car les domaines ne correspondent pas.

Quand devrais-je me protéger contre la falsification de requêtes intersites? Si vous pouvez vous assurer que vous ne mélangez pas les méthodes GET, POST et les autres méthodes de requête décrites ci-dessus, il serait judicieux de protéger toutes les POST requêtes par défaut.

Il n'est pas nécessaire de protéger les requêtes PUT et DELETE, car comme expliqué ci-dessus, un formulaire HTML standard ne peut pas être soumis par un navigateur utilisant ces méthodes.

En revanche, JavaScript peut en effet faire d’autres types de requêtes, par exemple. utilisez la fonction $ .ajax () de jQuery, mais n'oubliez pas que pour AJAX, les demandes d'utilisation des domaines doivent correspondre (tant que vous n'avez pas configuré explicitement votre serveur Web autrement).

Cela signifie que, souvent, vous n'avez même pas besoin d'ajouter un jeton CSRF aux demandes AJAX, même s'il s'agit de demandes POST, mais vous devez vous assurer de ne contourner que les demandes CSRF. vérifiez dans votre application Web si la demande POST est en réalité une demande AJAX. Vous pouvez le faire en recherchant la présence d'un en-tête comme X-Requested-With, que AJAX demandes incluent généralement. Vous pouvez également définir un autre en-tête personnalisé et vérifier sa présence côté serveur. C’est sûr, car un navigateur n’ajouterait pas d’en-têtes personnalisés à une soumission de formulaire HTML normale (voir ci-dessus). Par conséquent, M. Bad Guy n’a aucune chance de simuler ce comportement avec un formulaire.

Si vous avez des doutes sur les requêtes AJAX, car pour une raison quelconque, vous ne pouvez pas rechercher un en-tête comme X-Requested-With, transmettez simplement le jeton CSRF généré à votre code JavaScript et ajoutez-le au AJAX demande. Il y a plusieurs façons de le faire. soit l'ajouter à la charge, comme le ferait un formulaire HTML classique, ou ajouter un en-tête personnalisé à la demande AJAX. Tant que votre serveur sait où chercher dans une requête entrante et peut le comparer à la valeur d'origine qu'il se souvient de la session ou du cookie, vous êtes trié.

25
Dan

La racine de tout cela est de s'assurer que les demandes proviennent des utilisateurs réels du site. Un jeton csrf est généré pour les formulaires et doit être lié aux sessions de l'utilisateur. Il est utilisé pour envoyer des requêtes au serveur, sur lesquelles le jeton les valide. C’est un moyen de se protéger contre le csrf, un autre serait de vérifier l’en-tête du référent.

5
gladysbixly