Le cross-site scripting (XSS) se produit quand un site web transmet à votre navigateur le JavaScript de quelqu'un d'autre et que le navigateur l'exécute comme si le site l'avait écrit. Cette seule confusion, du code là où on attendait des données, suffit à voler une session, réécrire une page ou usurper les privilèges d'un utilisateur connecté. Le moyen le plus rapide de sentir à quel point c'est facile à déclencher est de casser vous-même une vraie entrée, alors ouvrez le lab XSS Playground de HackerDNA et faites apparaître une boîte alert avant de lire la théorie.
Le XSS figure dans la famille injection du Top 10 OWASP depuis que la liste existe, et il reste l'un des bugs web les plus signalés en 2026. Ce guide couvre les trois types que vous rencontrerez vraiment, ce qu'une attaque XSS permet à un attaquant de faire, les payloads qui le prouvent, et les défenses qui l'arrêtent. Si vous voulez l'attaque côte à côte avec sa cousine, notre comparatif XSS vs CSRF est la lecture complémentaire.
En résumé : le cross-site scripting (XSS) est une vulnérabilité web où une application renvoie des entrées utilisateur non échappées, de sorte que le script d'un attaquant s'exécute dans le navigateur d'un autre utilisateur, sous l'origine du site. Il se décline en trois formes : reflété (payload renvoyé dans une réponse), stocké (payload enregistré et servi à tout le monde) et basé sur le DOM (du JavaScript côté client écrit des données non fiables dans la page). Le correctif principal est l'encodage de sortie adapté au contexte, appuyé par une Content Security Policy et l'auto-échappement des frameworks. Le filtrage des entrées seul ne l'arrête pas.
Qu'est-ce que le cross-site scripting (XSS) ?
Le cross-site scripting est une vulnérabilité qui permet à un attaquant d'injecter des scripts côté client dans des pages que d'autres personnes consultent. Le navigateur ne peut pas distinguer le code injecté du code propre au site, alors il exécute le JavaScript de l'attaquant avec toute la confiance de l'origine du site : ses cookies, sa session, son DOM.
Le nom est un accident historique. Ce qui compte n'est pas que les scripts traversent des sites, c'est que les entrées utilisateur franchissent la frontière entre les données et le code exécutable. Un champ de commentaire, une barre de recherche, un paramètre d'URL, un nom d'utilisateur, tout ce qu'une page rend ensuite sans échappement peut transporter un payload que le navigateur exécute.
Le XSS correspond à CWE-79, et il est important parce qu'il s'exécute dans le contexte de la victime, pas celui du serveur. Un attaquant qui réussit un XSS sur une appli bancaire n'est pas sur le serveur, il est dans votre onglet connecté, capable de faire tout ce que vous pouvez y faire. C'est pourquoi il figure régulièrement parmi les découvertes les plus fréquentes dans les programmes de bug bounty.
Comment fonctionne une attaque XSS : la cause racine
Chaque bug XSS a la même origine que l'injection SQL : l'application mélange des entrées non fiables dans une sortie qu'elle interprète ensuite. Pour la SQLi, l'interpréteur est la base de données. Pour le XSS, c'est le navigateur, et le langage est le HTML plus le JavaScript.
Voici l'erreur sous sa forme la plus simple. Une page renvoie un terme de recherche directement dans le HTML :
<!-- Vulnérable : la requête est écrite dans la page sans échappement -->
<p>Vous avez recherché : <?php echo $_GET['q']; ?></p>
Envoyez q=chaussures et la page affiche "Vous avez recherché : chaussures". Envoyez q=<script>alert(1)</script> et le navigateur n'affiche pas ces caractères, il les analyse comme une vraie balise <script> et l'exécute. L'entrée a cessé d'être du texte pour devenir une partie de la page. Rien dans la chaîne n'indiquait au navigateur que la balise de l'attaquant était censée être affichée, pas exécutée.
C'est pourquoi le blocage par liste noire échoue ici tout autant qu'avec la SQLi. Les équipes tentent de retirer <script> et les attaquants sortent <img src=x onerror=alert(1)>, ou un svg, ou une URL JavaScript, ou un gestionnaire d'événement sur une balise que vous avez oubliée. Les entrées dangereuses sont pratiquement infinies parce que le problème n'est pas le payload, c'est que la sortie n'a jamais été encodée pour le contexte où elle a atterri.
Les trois types de XSS : reflété, stocké et basé sur le DOM
Quels sont les types de XSS ? Il y en a trois : le XSS reflété, où le payload est renvoyé dans la réponse immédiate ; le XSS stocké, où le payload est enregistré sur le serveur et servi à chaque visiteur ; et le XSS basé sur le DOM, où du JavaScript côté client écrit des données non fiables dans la page sans que le serveur voie jamais le payload. Ils diffèrent par l'endroit où l'entrée réside et par qui est touché.
XSS reflété
Le XSS reflété prend une entrée de la requête en cours, généralement un paramètre d'URL, et l'écrit directement dans la réponse. Le payload n'est stocké nulle part, l'attaque a donc besoin d'une étape de livraison : l'attaquant conçoit un lien malveillant et convainc une victime de cliquer dessus. Pensez à une page de recherche qui reflète votre requête, ou à une page d'erreur qui renvoie une valeur incorrecte.
Un payload reflété voyage dans l'URL :
https://shop.example/search?q=<script>alert(document.domain)</script>
Quiconque ouvre ce lien exécute le script dans sa propre session sur shop.example. Le XSS reflété est la variante la plus courante et celle sur laquelle vous tomberez en premier lors de vos tests, car tout paramètre renvoyé dans une page est un candidat.
XSS stocké
Le XSS stocké, aussi appelé XSS persistant, est le plus dangereux. Le payload est enregistré dans l'application, un commentaire, une bio de profil, un ticket de support, un avis produit, puis servi à tous ceux qui consultent ce contenu. Il n'y a aucun lien à envoyer. La victime charge simplement une page normale et le script de l'attaquant y est déjà, en attente.
Imaginez un forum où le corps d'un message est rendu sans encodage. Un attaquant publie un commentaire contenant un script, et chaque utilisateur qui ouvre le fil l'exécute. Un XSS stocké sur une page à fort trafic est proche d'un incident auto-propagé, ce qui est exactement la façon dont l'ancien ver Samy a ravagé MySpace en 2005, ajoutant plus d'un million d'amis en moins d'une journée.
XSS basé sur le DOM
Le XSS basé sur le DOM ne touche jamais la réponse du serveur. Le code vulnérable est du JavaScript qui s'exécute dans le navigateur et qui lit une source à laquelle il ne devrait pas se fier, comme location.hash ou document.referrer, puis la transmet à un puits qui exécute du markup, comme innerHTML ou document.write.
// Vulnérable : le hash est écrit dans la page en tant que HTML
document.getElementById('welcome').innerHTML =
'Bonjour, ' + decodeURIComponent(location.hash.slice(1));
Chargez la page avec #<img src=x onerror=alert(1)> et le navigateur construit cet élément image et déclenche le gestionnaire. Comme le payload vit dans le fragment après le #, il n'est souvent jamais envoyé au serveur, ce qui signifie que les filtres côté serveur et de nombreux WAF ne le voient jamais. À mesure que les applications monopages se sont imposées, le XSS DOM est passé d'un cas rare à une découverte de routine.
Ce que les attaquants font avec le XSS
Un alert(1) prouve le bug, mais ce n'est pas le but. Une fois qu'un attaquant peut exécuter du JavaScript dans votre session, il hérite de votre accès à la page. L'impact réaliste se répartit généralement en quelques catégories :
- Vol de session. Lire
document.cookieet l'envoyer ailleurs remet à l'attaquant votre session connectée, sauf si le cookie de session porte le marqueurHttpOnly. C'est ce seul drapeau qui rend le vol de cookie bien moins trivial que ne le suggèrent les tutoriels d'il y a dix ans. - Agir en tant que l'utilisateur. Même sans le cookie, le script peut effectuer des requêtes authentifiées en votre nom : changer votre e-mail, ajouter un administrateur, transférer des fonds. Il s'exécute depuis votre session, l'appli voit donc des actions légitimes d'un utilisateur légitime.
- Capture d'identifiants. Un script peut réécrire la page pour ajouter une fausse invite de connexion, ou un écouteur de frappe sur une vraie, récoltant ce que vous tapez dans un formulaire qui paraît totalement authentique.
- Défacement et hameçonnage. Comme le code contrôle le DOM, il peut remplacer le contenu, injecter des messages trompeurs ou rediriger vers une page contrôlée par l'attaquant, le tout sous le domaine de confiance affiché dans la barre d'adresse.
Quand vous testez de vraies applications, la découverte qui fait prendre un rapport au sérieux est rarement la boîte d'alerte. C'est la suite : prouver que le payload peut effectuer une action authentifiée, dans un lab ou un périmètre autorisé, pour que la gravité soit indéniable. C'est la différence entre "valeur reflétée" et "prise de contrôle de compte" dans un rapport de bug.
Payloads XSS et exemples
Vous n'avez pas besoin d'un payload exotique pour trouver du XSS. Vous avez besoin d'une sonde qui survit au contexte où elle atterrit. Commencez par injecter un marqueur inoffensif et regardez où et comment il revient dans le HTML source, puis façonnez le payload selon le contexte. Quelques sondes standard de preuve de concept, du genre utilisé chaque jour en test autorisé :
- La balise classique :
<script>alert(1)</script>fonctionne quand votre entrée atterrit entre des balises dans un corps HTML. - La sortie d'attribut :
"><svg onload=alert(1)>s'échappe d'une valeur placée à l'intérieur d'un attribut HTML. - Le gestionnaire d'événement :
<img src=x onerror=alert(1)>s'exécute quand<script>est filtré mais pas les autres balises. - Le contexte JavaScript :
';alert(1)//sort d'une chaîne écrite dans un bloc de script en ligne.
La leçon de cette liste, c'est le contexte. La même entrée est inoffensive à un endroit et exécutable à un autre, c'est pourquoi le correctif doit lui aussi tenir compte du contexte. Utilisez alert(document.domain) plutôt que alert(1) quand vous démontrez une découverte, car cela prouve sous quelle origine le script s'exécute et se lit clairement dans un rapport.
Pour une vraie référence organisée de payloads à essayer contre des cibles que vous êtes autorisé à tester, la liste PayloadsAllTheThings XSS maintenue par la communauté est celle que la plupart des testeurs gardent en favori. Traitez-la comme une aide à l'étude, pas une liste à balancer en masse.
Comment prévenir le XSS
Comment prévenir le cross-site scripting ? Encodez la sortie selon le contexte où elle apparaît, pour que les données non fiables soient toujours rendues comme du texte inerte plutôt qu'analysées comme du markup ou du script. L'encodage est la défense principale, et une Content Security Policy plus l'auto-échappement d'un framework moderne en font une défense en profondeur. La fiche de prévention XSS de l'OWASP détaille l'ensemble des règles ; voici ce qui change vraiment la donne.
- Encodage de sortie adapté au contexte. Échappez les données selon l'endroit où elles atterrissent. Encodez en HTML pour les corps d'éléments (
<devient<), encodez en attribut à l'intérieur des attributs, et ne placez jamais de données non fiables directement dans un bloc de script ou un gestionnaire d'événement. C'est le correctif qui met fin à la vulnérabilité. - Laissez le framework échapper par défaut. React, Angular et Vue encodent automatiquement les valeurs interpolées, c'est pourquoi les applis modernes ont moins de XSS que l'ère PHP. Le piège, c'est l'échappatoire :
dangerouslySetInnerHTML, lebypassSecurityTrustd'Angular et toute affectation brute d'innerHTMLvous font ressortir de la sécurité. Auditez chacune d'elles. - Déployez une Content Security Policy. Une CSP stricte qui bloque les scripts en ligne et restreint les sources est une solide seconde couche. Elle ne corrige pas un bug, mais elle peut empêcher un payload de s'exécuter ou d'appeler l'extérieur même quand un défaut passe entre les mailles.
- Activez HttpOnly sur les cookies de session. Cela empêche le JavaScript de lire le cookie, ce qui neutralise la forme la plus directe de vol de session. Associez-le à
Secureet à une valeurSameSiteraisonnable. - Assainissez uniquement quand vous devez vraiment rendre du HTML. Si les utilisateurs soumettent légitimement du texte enrichi, ne bricolez pas votre propre filtre. Passez-le par une bibliothèque éprouvée comme DOMPurify, conçue et testée précisément pour retirer le markup dangereux tout en gardant la mise en forme sûre.
Remarquez ce qui n'est pas en tête de cette liste : la validation des entrées. Filtrer les entrées est une couche de soutien utile, et rejeter des données manifestement mal formées est une bonne hygiène, mais cela ne peut pas être votre défense XSS. La même valeur est dangereuse dans un contexte de sortie et anodine dans un autre, alors le contrôle fiable vit à la sortie, où vous connaissez le contexte. Validez à l'entrée, encodez à la sortie.
Comment tester la présence de XSS
Comment trouver du XSS dans une application ? Injectez un marqueur unique dans chaque entrée, puis lisez le HTML brut de la réponse pour voir où il apparaît et s'il a été encodé. Là où votre marqueur revient intact et non échappé, façonnez un payload pour ce contexte précis et confirmez qu'il s'exécute. Une routine pratique :
- Cartographiez chaque entrée. Paramètres d'URL, champs de formulaire, en-têtes, cookies, et tout ce qu'une application monopage lit dans le fragment d'URL. Chacun est une source candidate.
- Injectez d'abord un marqueur, pas un payload. Envoyez une chaîne inoffensive comme
xss7391et cherchez-la dans la source de la réponse. Si elle apparaît encodée, ce contexte est défendu. Si elle apparaît brute, vous tenez une piste. - Adaptez le payload au contexte. Entre des balises, à l'intérieur d'un attribut ou dans un bloc de script, chacun demande une sortie différente. C'est là que comprendre les trois contextes paie.
- Automatisez l'ampleur. Un scanner dans Burp Suite ou un outil comme Dalfox va fuzzer des centaines de paramètres bien plus vite que vous à la main. Ne le pointez que vers des systèmes que vous possédez ou êtes autorisé à tester.
Le faire à la main quelques fois construit l'instinct de repérer une sortie non échappée lors d'une revue de code, là où la prévention est la moins chère. Apprendre à exploiter le XSS et apprendre à le prévenir sont la même compétence vue des deux côtés, et lancer l'attaque dans un lab contrôlé est le moyen le plus rapide de rendre l'habitude défensive automatique.
Considérations légales et éthiques
Rappel essentiel : obtenez toujours une autorisation écrite explicite avant de tester une application pour du XSS. Lancer des payloads contre un site que vous ne possédez pas est un accès non autorisé au titre du Computer Fraud and Abuse Act (États-Unis), du Computer Misuse Act (Royaume-Uni) et des lois équivalentes dans le monde, même quand votre payload est une simple boîte alert inoffensive.
- Testez uniquement sur des systèmes que vous possédez, sur des cibles d'entraînement volontairement vulnérables, ou dans le périmètre défini d'un bug bounty ou d'un engagement autorisé.
- Utilisez une preuve inoffensive comme
alert(document.domain)pour démontrer une découverte. Ne déployez pas de payloads qui volent les cookies ou les données de vrais utilisateurs pour prouver l'impact. - Si vous trouvez du XSS dans l'application de quelqu'un d'autre, signalez-le via son processus de divulgation et arrêtez-vous là.
- Les labs et cours liés ici existent pour que vous puissiez pratiquer l'attaque et la défense légalement, sur des cibles conçues pour exactement cela.
Questions fréquentes
Qu'est-ce que le cross-site scripting en termes simples ?
Le cross-site scripting (XSS) est une vulnérabilité web qui permet à un attaquant d'exécuter son propre JavaScript dans le navigateur d'un autre utilisateur. Cela arrive quand un site affiche une entrée utilisateur sans l'encoder, de sorte que le navigateur traite l'entrée comme du code plutôt que du texte. Le script s'exécute alors avec la confiance du site, donnant à l'attaquant l'accès à la session et à la page de cet utilisateur.
Quels sont les trois types de XSS ?
Le XSS reflété renvoie le payload dans la réponse immédiate, généralement depuis un paramètre d'URL, il a donc besoin qu'une victime clique sur un lien conçu. Le XSS stocké enregistre le payload sur le serveur et le sert à tous ceux qui consultent le contenu. Le XSS basé sur le DOM se produit entièrement dans du JavaScript côté client qui écrit des données non fiables dans la page, souvent sans que le serveur voie jamais le payload.
Le XSS est-il encore une menace en 2026 ?
Oui. Les frameworks modernes auto-échappent la sortie, ce qui a réduit le volume de XSS triviaux, mais le bug reste l'une des découvertes les plus courantes dans les programmes de bug bounty. Des échappatoires comme dangerouslySetInnerHTML, l'innerHTML brut dans les applications monopages et les puits basés sur le DOM maintiennent le XSS bien vivant sur les stacks actuels.
En quoi le XSS diffère-t-il de l'injection SQL ?
Les deux sont des bugs d'injection, mais ils visent des interpréteurs différents. Le XSS injecte du code qui s'exécute dans le navigateur de la victime, affectant la session de cet utilisateur. L'injection SQL injecte du code qui s'exécute dans la base de données, affectant les données côté serveur. Le XSS est une attaque côté client ; la SQLi est côté serveur. La cause racine commune est de mélanger des entrées non fiables dans une sortie qui est interprétée.
La validation des entrées arrête-t-elle le XSS ?
Pas à elle seule. Le blocage par liste noire de caractères ou de balises peut être contourné avec d'autres balises, des gestionnaires d'événements, des encodages et des URL JavaScript. La défense fiable est l'encodage de sortie adapté au contexte, pour que les données non fiables soient toujours rendues comme du texte inerte. Utilisez la validation des entrées comme couche de soutien, et ajoutez une Content Security Policy pour la défense en profondeur.
Fait partie de la série Top 10 OWASP
Articles liés :
- Tutoriel sur l'injection SQL
- Prévention de l'injection SQL
- XSS vs CSRF
- Cross-Site Scripting (XSS)
- Le contrôle d'accès défaillant expliqué
Vos prochaines étapes
Le cross-site scripting tient à une seule frontière : dès qu'un site laisse une entrée utilisateur devenir du code dans le navigateur de quelqu'un d'autre, l'attaquant est à l'intérieur de cette session. Les trois types, reflété, stocké et basé sur le DOM, ne sont que différents endroits où l'entrée peut franchir la ligne, et la défense est la même idée dans l'autre sens : encodez la sortie selon son contexte, laissez votre framework échapper par défaut, et ajoutez une Content Security Policy pour qu'un point oublié ne soit pas un cadeau gratuit. Le moyen de fixer cet instinct est de voir un payload s'exécuter une fois, puis de regarder l'encodage le lui retirer. Commencez avec l'offre gratuite de HackerDNA, sans carte bancaire, et faites apparaître votre premier script dans le lab XSS Playground. Quand vous voudrez le XSS dans le contexte de toute la surface d'attaque web, le chapitre XSS de notre cours Web Attacks le parcourt aux côtés de l'injection SQL, du CSRF et du reste du Top 10 OWASP dans des labs guidés dans le navigateur. Apprenez à lancer le payload, puis écrivez la page qui l'avale.