Pourquoi est-il courant de mettre des jetons de prévention CSRF dans les cookies?

J’essaie de comprendre toute la question avec CSRF et les moyens appropriés pour l’empêcher. (Ressources J’ai lu, compris et accepté: OSPAS, Fiche de prévention de la prévention CSR de l’OWASP , Questions sur le CSRF .)

Si je comprends bien, la vulnérabilité autour de CSRF est introduite par l’hypothèse que (du sharepoint vue du serveur web) un cookie de session valide dans une requête HTTP entrante reflète les souhaits d’un utilisateur authentifié. Mais tous les cookies pour le domaine d’origine sont attachés comme par magie à la requête par le navigateur, donc tout ce que le serveur peut déduire de la présence d’un cookie de session valide dans une requête est que la requête provient d’un navigateur qui a une session authentifiée; il ne peut rien supposer d’autre sur le code exécuté dans ce navigateur, ni sur le fait qu’il reflète réellement les souhaits de l’utilisateur. La manière d’empêcher cela consiste à inclure des informations d’authentification supplémentaires (le “jeton CSRF”) dans la requête, transscopes par d’autres moyens que la gestion automatique des cookies du navigateur. En gros, le cookie de session authentifie l’utilisateur / le navigateur et le jeton CSRF authentifie le code exécuté dans le navigateur.

En résumé, si vous utilisez un cookie de session pour authentifier les utilisateurs de votre application Web, vous devez également append un jeton CSRF à chaque réponse et exiger un jeton CSRF correspondant dans chaque requête (en mutation). Le jeton CSRF effectue ensuite un aller-retour entre le serveur et le navigateur et le serveur, prouvant ainsi au serveur que la page à l’origine de la demande est approuvée par (même généré) par ce serveur.

À ma question, qui concerne la méthode de transport spécifique utilisée pour ce jeton CSRF sur ce trajet.

Il semble courant (par exemple dans AngularJS , Django , Rails ) d’envoyer le jeton CSRF du serveur au client en tant que cookie (c’est-à-dire dans un en-tête Set-Cookie). comme un en-tête XSRF-TOKEN distinct à renvoyer au serveur.

(Une autre méthode est celle recommandée par ex. Express , où le jeton CSRF généré par le serveur est inclus dans le corps de la réponse via une extension de modèle côté serveur, directement attaché au code / marquage qui le fournira au serveur, par exemple comme une entrée de formulaire caché.Cet exemple est une manière plus web 1.0-ish de faire les choses, mais généraliserait bien à un client plus lourd de JS.)

Pourquoi est-il si courant d’utiliser Set-Cookie comme moyen de transport en aval pour le jeton CSRF? Pourquoi est-ce une bonne idée? J’imagine que les auteurs de tous ces frameworks ont considéré leurs options avec soin et ne se sont pas trompés. Mais à première vue, l’utilisation de cookies pour contourner ce qui est essentiellement une limitation de conception des cookies semble douteux. En fait, si vous utilisiez les cookies comme transport aller-retour (Set-Cookie: en-tête en aval du serveur pour indiquer au navigateur le jeton CSRF et Cookie: en-tête pour que le navigateur le renvoie au serveur), vous réintroduiriez la vulnérabilité essaient de réparer.

Je me rends compte que les frameworks ci-dessus n’utilisent pas de cookies pour tout le roundsortingp pour le jeton CSRF; ils utilisent Set-Cookie en aval, puis quelque chose d’autre (par exemple, un en-tête X-CSRF-Token) en amont, et cela ferme la vulnérabilité. Mais même en utilisant Set-Cookie comme le transport en aval est potentiellement trompeur et dangereux; le navigateur va maintenant attacher le jeton CSRF à chaque requête, y compris les véritables requêtes XSRF malveillantes; au mieux, cela rend la demande plus grande que nécessaire et au pire, une partie du code du serveur bien intentionnée mais erronée pourrait en fait essayer de l’utiliser, ce qui serait vraiment grave. De plus, puisque le destinataire réel du jeton CSRF est le Javascript côté client, cela signifie que ce cookie ne peut pas être protégé avec http uniquement. L’envoi du jeton CSRF en aval dans un en-tête Set-Cookie me semble donc sous-optimal.

Une bonne raison, que vous avez en quelque sorte abordée, est qu’une fois que le cookie CSRF a été reçu, il est alors disponible pour une utilisation tout au long de l’application dans le script client pour être utilisé dans les formulaires réguliers et les POST AJAX. Cela aura un sens dans une application JavaScript lourde telle que celle employée par AngularJS (utiliser AngularJS ne nécessite pas que l’application soit une application à une seule page, elle serait donc utile lorsque l’état doit circuler entre différentes requêtes de page où la valeur CSRF ne peut normalement pas persister dans le navigateur).

Considérez les scénarios et processus suivants dans une application classique pour connaître les avantages et inconvénients de chaque approche que vous décrivez. Ceux-ci sont basés sur le modèle de jeton de synchronisation .

Demander l’approche du corps

  1. L’utilisateur se connecte avec succès.
  2. Serveur émet un cookie d’authentification.
  3. L’utilisateur clique pour accéder à un formulaire.
  4. S’il n’est pas encore généré pour cette session, le serveur génère un jeton CSRF, le stocke sur la session utilisateur et le renvoie dans un champ masqué.
  5. L’utilisateur soumet un formulaire.
  6. Le serveur vérifie que le champ masqué correspond à la session stockée.

Avantages:

  • Simple à mettre en œuvre
  • Fonctionne avec AJAX.
  • Fonctionne avec des formes.
  • Le cookie peut être uniquement HTTP .

Désavantages:

  • Tous les formulaires doivent afficher le champ masqué en HTML.
  • Tous les POST AJAX doivent également inclure la valeur.
  • La page doit savoir à l’avance qu’elle nécessite le jeton CSRF afin de pouvoir l’inclure dans le contenu de la page afin que toutes les pages contiennent la valeur du jeton quelque part, ce qui peut prendre du temps à mettre en œuvre pour un grand site.

En-tête HTTP personnalisé (en aval)

  1. L’utilisateur se connecte avec succès.
  2. Serveur émet un cookie d’authentification.
  3. L’utilisateur clique pour accéder à un formulaire.
  4. La page étant chargée dans le navigateur, une demande AJAX est effectuée pour récupérer le jeton CSRF.
  5. Le serveur génère un jeton CSRF (s’il n’est pas déjà généré pour la session), le stocke sur la session utilisateur et le renvoie dans un en-tête.
  6. L’utilisateur soumet un formulaire (le jeton est envoyé via un champ masqué).
  7. Le serveur vérifie que le champ masqué correspond à la session stockée.

Avantages:

  • Fonctionne avec AJAX.
  • Le cookie peut être HTTP uniquement .

Désavantages:

  • Ne fonctionne pas sans une requête AJAX pour obtenir la valeur de l’en-tête.
  • Toutes les formes doivent avoir la valeur ajoutée dynamicment à leur code HTML.
  • Tous les POST AJAX doivent également inclure la valeur.
  • La page doit d’abord faire une demande AJAX pour obtenir le jeton CSRF, cela signifie donc un aller-retour supplémentaire à chaque fois.
  • Pourrait aussi bien sortir le jeton sur la page qui enregistrerait la requête supplémentaire.

En-tête HTTP personnalisé (en amont)

  1. L’utilisateur se connecte avec succès.
  2. Serveur émet un cookie d’authentification.
  3. L’utilisateur clique pour accéder à un formulaire.
  4. S’il n’est pas encore généré pour cette session, le serveur génère un jeton CSRF, le stocke sur la session utilisateur et le renvoie quelque part dans le contenu de la page.
  5. L’utilisateur soumet un formulaire via AJAX (le jeton est envoyé via l’en-tête).
  6. Le serveur vérifie que l’en-tête personnalisé correspond aux jetons stockés dans la session.

Avantages:

  • Fonctionne avec AJAX.
  • Le cookie peut être HTTP uniquement .

Désavantages:

  • Ne fonctionne pas avec les formulaires.
  • Tous les POST AJAX doivent inclure l’en-tête.

En-tête HTTP personnalisé (en amont et en aval)

  1. L’utilisateur se connecte avec succès.
  2. Serveur émet un cookie d’authentification.
  3. L’utilisateur clique pour accéder à un formulaire.
  4. La page étant chargée dans le navigateur, une demande AJAX est effectuée pour récupérer le jeton CSRF.
  5. Le serveur génère un jeton CSRF (s’il n’est pas déjà généré pour la session), le stocke sur la session utilisateur et le renvoie dans un en-tête.
  6. L’utilisateur soumet un formulaire via AJAX (le jeton est envoyé via l’en-tête).
  7. Le serveur vérifie que l’en-tête personnalisé correspond aux jetons stockés dans la session.

Avantages:

  • Fonctionne avec AJAX.
  • Le cookie peut être HTTP uniquement .

Désavantages:

  • Ne fonctionne pas avec les formulaires.
  • Tous les POST AJAX doivent également inclure la valeur.
  • La page doit d’abord faire une demande AJAX pour obtenir le jeton CRSF, cela signifie donc un aller-retour supplémentaire à chaque fois.

Set-Cookie

  1. L’utilisateur se connecte avec succès.
  2. Serveur émet un cookie d’authentification.
  3. L’utilisateur clique pour accéder à un formulaire.
  4. Le serveur génère un jeton CSRF, le stocke sur la session utilisateur et le renvoie à un cookie.
  5. L’utilisateur soumet un formulaire via AJAX ou via un formulaire HTML.
  6. Le serveur vérifie que l’en-tête personnalisé (ou le champ de formulaire masqué) correspond au jeton stocké de session.
  7. Cookie est disponible dans le navigateur pour être utilisé dans des demandes AJAX et de formulaire supplémentaires sans requêtes supplémentaires au serveur pour récupérer le jeton CSRF.

Avantages:

  • Simple à mettre en œuvre
  • Fonctionne avec AJAX.
  • Fonctionne avec des formes.
  • Ne nécessite pas nécessairement une requête AJAX pour obtenir la valeur du cookie. Toute requête HTTP peut la récupérer et elle peut être ajoutée à toutes les demandes de formulaires / AJAX via JavaScript.
  • Une fois que le jeton CSRF a été récupéré, comme il est stocké dans un cookie, la valeur peut être réutilisée sans requêtes supplémentaires.

Désavantages:

  • Toutes les formes doivent avoir la valeur ajoutée dynamicment à leur code HTML.
  • Tous les POST AJAX doivent également inclure la valeur.
  • Le cookie sera soumis pour chaque requête (c.-à-d. Tous les GET pour les images, CSS, JS, etc., qui ne sont pas impliqués dans le processus CSRF), ce qui augmente la taille de la demande.
  • Le cookie ne peut pas être uniquement HTTP .

L’approche par cookie est donc assez dynamic et permet de récupérer facilement la valeur du cookie (toute requête HTTP) et de l’utiliser (JS peut append la valeur automatiquement à n’importe quelle forme et peut être utilisée dans les requêtes AJAX comme en-tête ou valeur de forme). Une fois que le jeton CSRF a été reçu pour la session, il n’est plus nécessaire de le régénérer car un attaquant utilisant un exploit CSRF n’a aucune méthode pour récupérer ce jeton. Si un utilisateur malveillant tente de lire le jeton CSRF de l’utilisateur dans l’une des méthodes ci-dessus, cela sera empêché par la stratégie de même origine . Si un utilisateur malveillant tente de récupérer le serveur CSRF côté serveur (par exemple via curl ), ce jeton ne sera pas associé au même compte utilisateur car le cookie de la session d’authentification de la victime sera absent de la requête (il s’agira de l’attaquant). ne sera pas associé côté serveur à la session de la victime).

Outre le modèle de jeton Synchronizer, il existe également la méthode de prévention Double Submit Cookie CSRF, qui utilise bien sûr des cookies pour stocker un type de jeton CSRF. Ceci est plus facile à mettre en œuvre car il ne nécessite aucun état côté serveur pour le jeton CSRF. Le jeton CSRF pourrait en fait être le cookie d’authentification standard lors de l’utilisation de cette méthode, et cette valeur est soumise via les cookies comme d’habitude avec la demande, mais la valeur est également répétée dans un champ ou un en-tête masqué. ils ne peuvent pas lire la valeur en premier lieu. Il serait toutefois recommandé de choisir un autre cookie, autre que le cookie d’authentification, afin que le cookie d’authentification puisse être sécurisé en étant marqué HttpOnly. Donc, c’est une autre raison commune pour laquelle vous trouverez la prévention CSRF en utilisant une méthode basée sur les cookies.

L’utilisation d’un cookie pour fournir le jeton CSRF au client ne permet pas une attaque réussie car l’attaquant ne peut pas lire la valeur du cookie et ne peut donc pas mettre à la place de la validation CSRF côté serveur.

L’attaquant sera en mesure de provoquer une requête sur le serveur avec à la fois le cookie d’authentification et le cookie CSRF dans les en-têtes de requête. Mais le serveur ne recherche pas le jeton CSRF en tant que cookie dans les en-têtes de requête, mais dans la charge utile de la requête. Et même si l’attaquant sait où placer le jeton CSRF dans la charge utile, il devrait lire sa valeur pour le placer là. Cependant, la stratégie d’origine du navigateur empêche la lecture de toute valeur de cookie sur le site Web cible.

La même logique ne s’applique pas au cookie de jeton d’authentification, car le serveur l’attend dans les en-têtes de requête et l’attaquant n’a rien à faire de particulier pour le placer là.

Ma meilleure estimation quant à la réponse: considérez ces 3 options pour savoir comment faire passer le jeton CSRF du serveur au navigateur.

  1. Dans le corps de la requête (pas d’en-tête HTTP).
  2. Dans un en-tête HTTP personnalisé, pas Set-Cookie.
  3. En tant que cookie, dans un en-tête Set-Cookie.

Je pense que le premier, le corps de la demande (bien que démontré par le tutoriel Express que j’ai lié à la question ), ne soit pas aussi portable dans une grande variété de situations; tout le monde ne génère pas dynamicment chaque réponse HTTP; où vous finirez par avoir besoin de placer le jeton dans la réponse générée peut varier considérablement (dans une entrée de formulaire cachée; dans un fragment de code JS ou une variable accessible par un autre code JS; peut-être même dans une URL mettre des jetons CSRF). Ainsi, bien que réalisable avec une certaine personnalisation, le n ° 1 est difficile à mettre en place pour une approche unique.

Le second, en-tête personnalisé, est attrayant mais ne fonctionne pas, car si JS peut obtenir les en-têtes d’un XHR appelé, il ne peut pas obtenir les en-têtes de la page à partir de laquelle il a été chargé .

Cela laisse le troisième, un cookie porté par un en-tête Set-Cookie, comme une approche facile à utiliser dans toutes les situations (le serveur de n’importe quel utilisateur pourra définir des en-têtes de cookie par requête, peu importe le type de les données sont dans le corps de la requête). Donc, malgré ses inconvénients, c’était la méthode la plus simple pour les frameworks à mettre en œuvre largement.