Cette page a été traduite à partir de l'anglais par la communauté.Vous pouvez contribuer en rejoignant la communauté francophone sur MDN Web Docs.
Falsification de requête intersites (CSRF)
Dans une attaque de falsification de requête intersites (cross-site request forgery, CSRF), un·e attaquant·e amène l'utilisateur·ice ou le navigateur à émettre une requête HTTP vers le site visé depuis un site malveillant. La requête inclut les informations d'identification de l'utilisateur·ice et amène le serveur à exécuter une action nuisible, pensant que l'utilisateur·ice l'a voulue.
Dans cet article
Vue d'ensemble
Un site web effectue généralement des actions particulières pour le compte d'un·e utilisateur·ice — acheter un produit ou prendre un rendez-vous, par exemple — en recevant une requête HTTP depuis le navigateur de l'utilisateur·ice, souvent avec des paramètres détaillant l'action à réaliser. Pour s'assurer que la requête provient bien de la personne visée, le serveur s'attend à ce qu'elle inclue descredentials pour l'utilisateur·ice : par exemple, un cookie contenant l'identifiant de session.
Dans l'exemple ci‑dessous, l'utilisateur·ice s'est déjà connecté·e à sa banque et le navigateur a enregistré un cookie de session. La page contient un élément<form>, qui permet de transférer des fonds à une autre personne. Lorsque l'utilisateur·ice soumet le formulaire, le navigateur envoie une requêtePOST au serveur, incluant les données du formulaire. Si l'utilisateur·ice est connecté·e, la requête inclut le cookie de l'utilisateur·ice. Le serveur valide le cookie et réalise l'action particulière — ici, le virement :
Dans ce guide, nous appellerons une telle requête, qui exécute une action particulière, unerequête modifiant l'état.
Dans une attaque CSRF, l'attaquant·e crée un site contenant un formulaire. L'attribut action du formulaire pointe vers le site de la banque, et le formulaire contient des champs cachés imitant ceux de la banque :
<form action="https://ma-banque.exemple.org/transfer" method="POST"> <input type="hidden" name="recipient" value="attacker" /> <input type="hidden" name="amount" value="1000" /></form>La page contient aussi du JavaScript qui soumet le formulaire au chargement de la page :
const form = document.querySelector("form");form.submit();Lorsque l'utilisateur·ice visite la page, le navigateur soumet le formulaire au site de la banque. Comme l'utilisateur·ice est connecté·e à sa banque, la requête peut inclure le vrai cookie de l'utilisateur·ice, si bien que le serveur de la banque valide la requête et transfère les fonds :
Il existe d'autres moyens pour l'attaquant·e d'émettre une falsification de requête intersites. Par exemple, si le site utilise une requêteGET pour effectuer l'action, l'attaquant·e peut éviter d'utiliser un formulaire et exécuter l'attaque en envoyant à la personne visée un lien vers une page qui contient un balisage comme ceci :
<img src="https://ma-banque.exemple.org/transfer?recipient=attacker&amount=1000" />Lorsque l'utilisateur·ice charge la page, le navigateur tente de récupérer la ressource d'image, qui est en réalité la requête de transaction.
De manière générale, une attaque CSRF est possible si votre site :
- Utilise des requêtes HTTP pour modifier un état côté serveur.
- Utilise uniquement des cookies pour vérifier que la requête provient d'un·e utilisateur·ice authentifié·e.
- Utilise uniquement des paramètres dans la requête que l'attaquant·e peut prédire.
Défenses contre CSRF
Dans cette section, nous décrivons trois défenses alternatives contre CSRF et une quatrième pratique à utiliser endéfense en profondeur qui complète l'une ou l'autre.
La première défense principale consiste àutiliser desjetons CSRF intégrés à la page. C'est la méthode la plus courante si vous émettez des requêtes modifiant l'état depuis des formulaires, comme dans l'exemple ci‑dessus.
La deuxième consiste àutiliser lesmétadonnées Fetch via des en‑têtes HTTP pour vérifier si la requête modifiant l'état est émise intersite ou non.
La troisième consiste à s'assurer que les requêtes modifiant l'étatne sont pas desrequêtes simples, afin que les requêtes intersites soient bloquées par défaut. Cette méthode convient si vous émettez des requêtes modifiant l'état via des API JavaScript comme
fetch().
Enfin, nous aborderonsl'attribut de cookieSameSite, qui peut servir de défense en profondeur aux côtés de l'une des méthodes précédentes.
Jetons CSRF
Dans cette défense, lorsque le serveur sert une page, il y intègre une valeur imprévisible appeléejeton CSRF. Ensuite, lorsque la page légitime envoie la requête modifiant l'état au serveur, elle inclut le jeton CSRF dans la requête HTTP. Le serveur peut alors vérifier la valeur du jeton et n'exécute la requête que si elle correspond. Comme un·e attaquant·e ne peut pas deviner la valeur du jeton, il·elle ne peut pas émettre une falsification réussie. Même si l'attaquant·e découvre un jeton après son utilisation, la requête ne peut pas être rejouée si le jeton change à chaque fois.
Pour les soumissions de formulaire, le jeton CSRF est généralement inclus dans un champ caché, de sorte qu'il soit automatiquement renvoyé au serveur pour vérification lors de la soumission du formulaire.
Pour une API JavaScript commefetch(), le jeton peut être placé dans un cookie ou intégré à la page, puis JavaScript extrait la valeur et l'envoie dans un en‑tête supplémentaire.
Les frameworks web modernes proposent généralement une prise en charge intégrée des jetons CSRF : par exemple,Django(angl.) permet de protéger les formulaires à l'aide de la balisecsrf_token(angl.). Cette balise génère un champ de formulaire caché supplémentaire contenant le jeton, que le framework vérifie ensuite côté serveur.
Pour tirer parti de cette protection, vous devez comprendre tous les endroits de votre site où vous utilisez des requêtes HTTP modifiant l'état et vous assurer d'utiliser la défense fournie par votre framework choisi.
Métadonnées Fetch
Les métadonnées Fetch sont un ensemble d'en‑têtes de requête HTTP, ajoutés par le navigateur, qui fournissent des informations supplémentaires sur le contexte d'une requête HTTP. Le serveur peut utiliser ces en‑têtes pour décider d'autoriser ou non une requête.
Le plus pertinent pour CSRF est l'en‑têteSec-Fetch-Site, qui indique au serveur si la requête estsame-origin,same-site,cross-site ou initiée directement par l'utilisateur·ice. Le serveur peut utiliser cette information pour autoriser les requêtes d'origine autorisée ou les bloquer comme des attaques CSRF potentielles.
Par exemple, ce codeExpress n'autorise que les requêtessame-site etsame-origin :
app.post("/transfer", (req, res) => { const secFetchSite = req.headers["sec-fetch-site"]; if (secFetchSite === "same-origin" || secFetchSite === "same-site") { console.log("allowed"); // Met à jour de l'état } else { console.log("denied"); // Ne met pas à jour l'état }});VoirFetch metadata request header pour la liste complète des en‑têtes de métadonnées Fetch, etProtéger vos ressources contre les attaques web avec Fetch Metadata(angl.) pour un guide d'utilisation.
Éviter les requêtes simples
Les navigateurs distinguent deux types de requêtes HTTP : lesrequêtes simples et les autres.
Les requêtes simples, qui sont le type de requête émise par la soumission d'un élément<form>, peuvent être effectuées intersites sans être bloquées. Puisque les formulaires peuvent émettre des requêtes intersites depuis les débuts du web, il est important pour la compatibilité qu'ils puissent toujours le faire. C'est pourquoi nous devons implémenter d'autres stratégies pour protéger les formulaires contre CSRF, comme l'utilisation d'un jeton CSRF.
En revanche, d'autres parties de la plateforme web, en particulier des API JavaScript commefetch(), peuvent émettre d'autres types de requêtes (par exemple, des requêtes qui définissent des en‑têtes personnalisés), et ces requêtes ne sont pas autorisées intersites par défaut, de sorte qu'une attaque CSRF ne réussirait pas.
Ainsi, un site web qui utilisefetch() ouXMLHttpRequest peut se protéger contre CSRF en s'assurant que les requêtes modifiant l'état qu'il émet ne sont jamais des requêtes simples.
Par exemple, définir l'Content-Type de la requête à"application/json" empêchera son traitement en tant que requête simple :
fetch("https://ma-banque.exemple.org/transfer", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ recipient: "joe", amount: "100" }),});De même, définir un en‑tête personnalisé sur la requête empêchera son traitement comme requête simple :
fetch("https://ma-banque.exemple.org/transfer", { method: "POST", headers: { "X-MA-BANQUE-ANTI-CSRF": 1, }, body: JSON.stringify({ recipient: "joe", amount: "100" }),});Le nom de l'en‑tête peut être quelconque, tant qu'il n'entre pas en conflit avec des en‑têtes standards.
Le serveur peut alors vérifier l'existence de l'en‑tête : s'il existe, le serveur sait que la requête n'a pas été traitée comme une requête simple.
Requêtes non simples et CORS
Nous avons dit que les requêtes non simples ne sontpar défaut pas envoyées intersites. Le problème est que le protocolePartage des ressources entre origines (CORS) permet à un site d'assouplir cette restriction.
Plus précisément, votre site sera vulnérable à une attaque CSRF depuis une origine donnée si sa réponse à une requête modifiant l'état inclut :
- L'en‑tête
Access-Control-Allow-Originet que cet en‑tête liste l'origine émettrice ; - L'en‑tête
Access-Control-Allow-Credentials.
Défense en profondeur : cookies SameSite
L'attribut de cookieSameSite apporte une certaine protection contre les attaques CSRF. Ce n'est pas une défense complète, et il convient de la considérer comme un complément à l'une des autres défenses, pour fournir une certaine défense en profondeur.
Cet attribut contrôle quand un navigateur est autorisé à inclure le cookie dans une requête intersite. Il a trois valeurs possibles :None,Lax etStrict.
La valeurStrict offre la protection la plus forte : si cet attribut est défini, le navigateur n'inclura le cookie dans aucune requête intersite. Cependant, cela crée un problème d'ergonomie : si l'utilisateur·ice est connecté·e à votre site et suit un lien vers votre site depuis un autre site, alors vos cookies ne seront pas inclus, et l'utilisateur·ice ne sera pas reconnu·e à l'arrivée sur votre site.
La valeurLax assouplit cette restriction : les cookies sont inclus dans les requêtes intersites si les deux conditions suivantes s'appliquent :
- La requête est une navigation du contexte de navigation de niveau supérieur.
- La requête a utilisé une méthodesafe : notablement,
GETest sûre, maisPOSTne l'est pas.
Cependant,Lax offre une protection nettement plus faible queStrict :
- Un·e attaquant·e peut déclencher une navigation de premier niveau. Par exemple, au début de cet article, nous montrons une attaque CSRF où l'attaquant·e soumet un formulaire vers la cible : cela est considéré comme une navigation de premier niveau. Si le formulaire était soumis avec
GET, alors la requête inclurait tout de même des cookies avecSameSite=Lax. - Même si le serveur vérifie que la requête n'a pas été envoyée avec
GET, certains frameworks web prennent en charge la « surcharge de méthode » (method override) : cela permet à un·e attaquant·e d'envoyer une requête avecGETtout en la faisant apparaître côté serveur comme si elle utilisaitPOST.
De façon générale, vous devriez essayer d'utiliserStrict pour certains cookies etLax pour d'autres :
Laxpour les cookies que vous utilisez afin de décider si un·e utilisateur·ice connecté·e doit se voir afficher une page ;Strictpour les cookies utilisés pour des requêtes modifiant l'état que vous ne souhaitez pas autoriser intersites.
Un autre problème avec l'attributSameSite est qu'il protège des requêtes provenant d'un autresite, et non d'une autreorigin. C'est une protection plus laxiste, car (par exemple)https://foo.example.org ethttps://bar.example.org sont considérés comme le même site, bien qu'ils soient des origines différentes. En pratique, si vous comptez sur la protectionsame‑site, vous devez faire confiance à tous les sous‑domaines de votre site.
VoirContournement des restrictions de cookies SameSite(angl.) pour plus de détails sur les limites deSameSite.
Liste de contrôle récapitulative de la défense
Nous pouvons résumer les défenses ci‑dessus ainsi :
- Comprendre où, dans votre site, vous implémentez des requêtes modifiant l'état qui utilisent des cookies de session pour déterminer quel·le utilisateur·ice a émis la requête.
- Mettre en œuvre au moins une des défenses principales décrites dans ce document :
- Si vous utilisez des éléments
<form>pour émettre ces requêtes, assurez‑vous d'utiliser un framework qui prend en charge les jetons CSRF et utilisez‑le. - Si vous utilisez des API JavaScript comme
fetch()ouXMLHttpRequestpour émettre des requêtes modifiant l'état, assurez‑vous qu'elles ne soient pas des requêtes simples. - Quel que soit le mécanisme utilisé pour émettre les requêtes, envisagez d'utiliser les métadonnées Fetch pour interdire les requêtes intersites.
- Si vous utilisez des éléments
- Éviter d'utiliser la méthode
GETpour émettre des requêtes modifiant l'état. - Définir l'attribut
SameSitedes cookies de session surStrictsi vous le pouvez, ouLaxsi nécessaire.