Le DOM-based XSS est une vulnérabilité de cross-site scripting qui réside entièrement dans le code client-side. Le propre JavaScript de la page lit une entrée contrôlable par l'attaquant — le plus souvent depuis l'URL — et l'écrit dans le DOM via un sink non sécurisé comme innerHTML, exécutant ainsi le script de l'attaquant. Contrairement au XSS réfléchi ou stocké, le payload malveillant peut n'apparaître dans aucune réponse du serveur.
Comment fonctionne une attaque DOM XSS ?
La vulnérabilité est un flux de données depuis une source contrôlée par l'attaquant vers un sink qui interprète le texte comme du code ou du balisage :
// vulnerable: fragment flows straight into a sink
const name = decodeURIComponent(location.hash.slice(1));
document.querySelector("#welcome").innerHTML = "Hello " + name;
// https://example.com/#<img src=x onerror=stealCookies()>
Sources courantes : location.hash, location.search, document.referrer, window.name, les données de postMessage et le stockage client-side. Sinks courants : innerHTML/outerHTML, document.write, eval et Function, le .html() de jQuery, et les écritures d'attributs qui créent des gestionnaires ou des URLs (onclick, href avec javascript:).
Comme le fragment de l'URL n'est jamais envoyé au serveur, un payload placé après # peut exploiter la page pendant que le serveur voit une requête parfaitement normale.
XSS réfléchi vs stocké vs DOM-based
| XSS réfléchi | XSS stocké | DOM-based XSS | |
|---|---|---|---|
| Où vit le payload | Dans la requête, renvoyé par le serveur | Dans la base de données du serveur | Dans l'entrée client-side (souvent le fragment de l'URL) |
| La réponse du serveur contient-elle le payload ? | ✓ | ✓ | Souvent ✗ |
| Visible pour les WAF / logs serveur ? | Généralement | Généralement | Souvent non |
| Corrigé où | Encodage de sortie côté serveur | Assainissement + encodage côté serveur | Code client-side : sinks sûrs, assainissement |
Pourquoi le DOM XSS est facile à manquer
Les défenses côté serveur inspectent les requêtes et les réponses ; le DOM XSS peut contourner les deux. Les frameworks réduisent le risque — React et consorts échappent par défaut — mais dangerouslySetInnerHTML, le mauvais usage des templates et les scripts tiers présents sur la page le réintroduisent. Et une page moderne exécute des dizaines de scripts que vous n'avez pas écrits : un sink non sécurisé dans n'importe lequel d'entre eux est un sink non sécurisé sur votre page. Ce problème de composition est au cœur de la sécurité client-side.
Comment le prévenir et le détecter
- Préférez les sinks sûrs :
textContentplutôt queinnerHTML; évitez entièrementevalet les gestionnaires construits à partir de chaînes. - Assainissez quand le HTML est inévitable (DOMPurify ou l'émergente Sanitizer API), et adoptez Trusted Types là où ils sont pris en charge — ils transforment les écritures vers des sinks non sécurisés en violations de politique.
- Déployez une Content Security Policy stricte avec des nonces ; elle bloque de nombreux styles de payload, mais pas toute la classe d'attaque.
- Surveillez le comportement à l'exécution : l'exploitation finit par se manifester sous la forme de scripts faisant des choses qu'ils ne devraient pas — nouvelles requêtes sortantes, écritures dans le DOM vers des formulaires de paiement, listeners inattendus. La surveillance client-side de cside observe les sessions réelles au niveau du navigateur, qui est le seul endroit où une attaque limitée au DOM est visible tout court.







