Avertissement : Cet article a été traduit de l’anglais par un LLM. La précision n’est pas garantie. Vous pouvez lire l’article original en anglais.
Le Cross-Site Request Forgery, ou CSRF, est une attaque qui permet de cibler des applications web. Typiquement, elle consiste à faire exécuter involontairement à un utilisateur une action sur un site où il est connecté.
Dans cet article, nous allons discuter de certaines des façons dont une telle attaque peut être menée, et comment il est possible de s’en protéger. Cet article vise à fournir une introduction au sujet, donc toutes les possibilités d’exploitation et les contre-mesures ne seront pas abordées. Si vous souhaitez en savoir plus, plusieurs références à la fin de l’article traitent du sujet en profondeur.
Comment mener une attaque CSRF ?#
Un exemple simple#
Imaginons que Bob et Alice sont tous les deux membres d’un forum. Pour une raison quelconque, Bob n’aime pas ce qu’Alice dit et décide de lui donner une leçon en faisant supprimer son compte. La plateforme utilise un logiciel open source, et en examinant le code, Bob peut voir que lors de la suppression d’un utilisateur, la requête GET suivante est effectuée : /users/delete?userName=[userName]&confirm=True.
Sur cette base, Bob va envoyer un email à l’administrateur du forum, et en utilisant un prétexte, va l’amener à cliquer sur l’URL forum.tld/delete?userName=Alice&confirm=True. Puisque l’administrateur sera connecté au forum en cliquant sur ce lien, la fonction de suppression du compte d’Alice sera appelée, et le compte sera effectivement supprimé.
Maintenant, le compte sera supprimé, mais l’administrateur aura un message d’erreur disant quelque chose comme « Le compte a été supprimé avec succès », ce qui n’est pas très discret. L’administrateur réalisera ce qui s’est passé et bannira Bob.
Mais y avait-il un moyen pour Bob de mener l’attaque sans être aussi facilement détecté ? Oui. Au lieu de simplement partager le lien dans son email, il aurait pu envoyer un email formaté en HTML incluant une image de 0x0 px :
<img src="http://forum.tld/delete?userName=Alice&confirm=True" width="0" height="0" border="0">
Si l’administrateur ouvrait l’email HTML dans le même navigateur où il est connecté au forum, la requête aurait été exécutée et il n’aurait pas pu remarquer ce qui venait de se passer.
Notez que dans cet exemple, nous mentionnons des requêtes GET, mais il est possible d’exploiter cette faille via une requête POST également. Pour ce faire, il faudrait créer une page web avec un formulaire en HTML et le faire se soumettre automatiquement lorsque la page est chargée.
En bref#
Pour résumer, trois éléments essentiels doivent être présents pour qu’une attaque CSRF réussisse :
- La requête HTTP doit être effectuée depuis le navigateur où la victime est authentifiée sur le site web cible
- L’attaquant connaît les paramètres attendus dans la requête et ce qu’ils doivent être. Tous les paramètres doivent être prévisibles
- L’application cible repose sur des cookies de session
Comment protéger votre service contre les attaques CSRF#
La façon la plus courante de traiter les attaques CSRF est d’utiliser des jetons CSRF, qui sont une chaîne de caractères imprévisible (pour un tiers) générée côté serveur pour chaque requête, et validée par le serveur pour chaque requête.
Ces jetons doivent être ajoutés à chaque requête modifiant un état du système, et ne doivent pas être passés en GET (c’est-à-dire qu’ils ne doivent pas être présents dans l’URL). La raison principale est que tout ce qui est passé en GET peut être enregistré à divers endroits et transmis avec les requêtes HTTP. Par conséquent, aucune opération modifiant l’état ne devrait utiliser des requêtes GET. À la place, il est par exemple possible de passer le jeton en utilisant une valeur cachée dans un formulaire :
<form action="//deluser" method="POST">
<input type="hidden" name="csrf-token" value="8927dd0eeb9b65500d148bdf7a144b598fc161c974d86e1ec18174ca7813ee8483f56035e28779aae83e11017b29fe08208b09d515cafb214e5defdbace07f36" />
<input type="text" name="username" />
<input type="submit" name="Delete the User" />
</form>Une solution permettant de gérer la protection CSRF sans serveur est de mettre toutes les informations nécessaires pour valider la requête dans le jeton lui-même, puis de le chiffrer. Par exemple, nous pourrions créer une chaîne au format sessionId timestamp, la chiffrer, puis l’utiliser comme jeton.
Lorsque les utilisateurs font une requête, le serveur n’a qu’à déchiffrer le jeton, vérifier que l’identifiant de session appartient à l’utilisateur actuel et que l’horodatage se situe dans la fenêtre de temps attendue. Si les deux éléments sont corrects, alors le serveur peut poursuivre.
L’intérêt d’utiliser un horodatage est de prévenir les attaques par rejeu. Par exemple, nous pourrions définir une règle disant que nous attendons que l’horodatage ne soit pas plus ancien que 5 minutes. Ensuite, si nous recevons un jeton avec un horodatage vieux de 10 minutes, la requête serait refusée.
En plus de cette sécurité, il serait possible de demander les mots de passe des utilisateurs, ou, si votre application implémente le TOTP, de demander un jeton pour valider une opération critique.
Autres méthodes de protection#
Double soumission de cookie#
Une méthode populaire pour protéger les services contre les attaques CSRF est d’utiliser un cookie à double soumission. Pour chaque requête, le serveur va générer un cookie avec un jeton, et mettre également ce jeton dans la requête (par exemple, dans un champ caché de formulaire). Cette méthode repose sur l’hypothèse qu’il n’est pas possible d’écrire un cookie depuis un autre domaine, donc si le cookie et les champs cachés ont la même valeur, alors la requête doit être valide.
C’est vrai, mais il y a plusieurs failles. L’une d’elles survient si vous ne contrôlez pas tous les sous-domaines de votre nom de domaine, car les sous-domaines peuvent écrire des cookies dans le domaine principal, et il n’est pas possible de distinguer facilement ce qui est écrit où. Par exemple, subdom1.dom.tld peut écrire un cookie sous dom.tld. À partir de là, si un attaquant contrôle subdom1.dom.tld, et que votre site web est hébergé sur subdom2.dom.tld, il peut écrire le cookie en utilisant subdom1, et il sera lu par votre application comme un cookie légitime généré par subdom2.
Si vous vous demandez pourquoi l’utilisation d’un simple cookie ne suffit pas, c’est parce que les cookies sont toujours envoyés lorsque vous accédez à un site web, indépendamment de l’origine. Une façon d’atténuer ce problème (mais pas tous les autres) serait d’utiliser l’attribut SameSite Cookie, qui peut empêcher l’envoi de cookies dans certains scénarios. Notez que l’attribut SameSite Cookie devrait être implémenté en complément d’autres mesures, car il n’est pas suffisant en lui-même.
Utiliser l’en-tête Referer#
Une autre solution pour prévenir les CSRF serait de vérifier l’en-tête origin/referer lors de la validation des requêtes, et d’exécuter la requête si l’en-tête indique que la requête provient de notre site web. Cela n’est cependant pas considéré comme suffisant en soi, car cela peut rencontrer de multiples problèmes tels que ces en-têtes étant définis à null pour des raisons de confidentialité, ou supprimés par des proxys.
Une solution quelque peu similaire, lors de l’utilisation de JavaScript, serait de définir des en-têtes personnalisés lors des requêtes. Cela fonctionnerait pour prévenir les CSRF car grâce à la politique de même origine (SOP) implémentée dans les navigateurs, il ne serait pas possible pour un adversaire de créer cet en-tête.
Sources#
- Bypassing CSRF Protections - A Double Defeat of the Double-Submit Cookie Pattern
- NetSparker: Using the Same-Site Cookie Attribute to Prevent CSRF Attacks
- NCC Group: Common CSRF Prevention Misconceptions
- OWASP: Cross Site Request Forgery
- OWASP: Cross-Site Request Forgery Prevention Cheat Sheet
- Play Framework: Protecting against Cross Site Request Forgery
- PortSwigger: What is CSRF?
- PortSwigger: CSRF Tokens
- PortSwigger: Defending against CSRF with SameSite cookies
- Stack Overflow: Why is the same origin policy so important?