Le Cross-Site Scripting (XSS) est une vulnérabilité de sécurité web permettant à un attaquant d'injecter du code malveillant dans une page visitée par d'autres utilisateurs. Cette faille peut compromettre la confidentialité, l'intégrité et la sécurité des utilisateurs et des systèmes concernés.
Qu'est-ce qu'une faille XSS ?
Une faille XSS survient lorsqu'une application web insère du contenu manipulable par un utilisateur de manière non filtré ou non encodé dans une page HTML. Ce contenu peut inclure du code JavaScript qui sera exécuté dans le navigateur des utilisateurs. On distingue trois principaux types de XSS :
- XSS Reflected : injection via un paramètre transmis à la volée.
- XSS Stored : injection enregistrée côté serveur (ex. : base de données).
- XSS DOM-Based : injection exploitant une modification du DOM côté client par JavaScript.
De manière générale, les XSS permettent de faire tout ce qui est possible avec JavaScript dans le contexte de la page web. Il est donc possible de voler des cookies (s'ils n'ont pas le flag HTTPOnly), rediriger vers d'autres sites, afficher des messages d'alerte, insérer un faux formulaire de connexion, pour voler des comptes, etc.
XSS Reflected
Ce type d'attaque repose sur l'injection de code dans l'URL ou une requête, qui est immédiatement réutilisé par la page web sans traitement sécurisé et sans avoir été stocké.
Ce type d'attaque demande donc souvent une interaction de l'utilisateur, par exemple en cliquant sur un lien qui contient le code malveillant (ou payload).
Exemple d'URL vulnérable :
http://example.com/search?q=<script>alert(1)</script>
Code vulnérable :
<?php
if (isset($_GET['q'])) {
echo '<p>Search results for ' . $_GET['q'] . '</p>';
}
?>
Pourquoi c'est vulnérable : Le contenu de $_GET['q']
est inséré directement dans le HTML sans être encodé. Si l'utilisateur injecte une balise <script>
, qui permet d'insérer du code JavaScript directement dans la page, celle-ci sera exécutée par le navigateur.
XSS Stocké (Stored XSS)
Ici le code malveillant est stocké de façon persistante (base de données, fichier, etc.) et sera exécuté à chaque affichage de la page concernée.
Par exemple, si les commentaires d'un blog sont vulnérables, un attaquant peut poster un commentaire contenant du code JavaScript. Chaque fois qu'un utilisateur affichera une page qui contient ce commentaire, le code sera exécuté dans son navigateur.
Exemple de requête malveillante :
POST /post/comment?id=1 HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
message=<script>alert(1)</script>
Code vulnérable :
// Affichage du commentaire
foreach ($comments as $comment) {
echo '<p>' . $comment->message . '</p>';
}
Pourquoi c'est vulnérable : Le commentaire est affiché sans encodage HTML. Une balise <script>
injectée dans le champ message
sera interprétée par le navigateur à chaque chargement.
XSS DOM-Based
Cette faille repose sur la modification du DOM via du JavaScript côté client, utilisant des données potentiellement non fiables.
Elle est généralement plus complexe à détecter, car le code malveillant n'est pas inséré directement dans le HTML par le serveur, mais, après que la page ait été envoyée et chargée, par le JavaScript exécuté dans le navigateur.
Code vulnérable :
<script>
const q = new URLSearchParams(window.location.search).get("q");
if (q) {
document.getElementById("results").innerHTML = "<p>Search results for: " + q + "</p>";
}
</script>
Exemple d’URL :
http://example.com/search?q=<img src=1 onerror=alert(1)>
Pourquoi c'est vulnérable : La méthode innerHTML
insère directement du HTML interprétable. Par ailleurs, dans cet exemple, on n'a pas utilisé de balise HTML script
car il est possible que son contenu ne soit pas interprété par le navigateur. On a donc utilisé une balise img
avec un events onerror
pour exécuter le code JavaScript.
En effet, de nombreux events peuvent être utilisés dans les balises HTML, comme onload
, onclick
ou onmouseover
, pour exécuter du code JavaScript en réaction à un événement.
Ici, on utilise onerror
, qui est déclenché lorsqu'une erreur se produit lors du chargement d'une image. En injectant une image invalide, le code JavaScript dans l'attribut onerror
sera exécuté.
N'hésitez pas à consulter cette page du site de Portswigger (l'éditeur de BurpSuite), pour en savoir plus : XSS Cheat Sheet.
Corriger et prévenir les XSS
De manière générale, pour prévenir les failles XSS, il est essentiel d'encoder en HTML toutes données manipulables par l'utilisateur avant de l'afficher dans une page web. Cela inclut les données provenant des formulaires, des cookies, des en-têtes HTTP, etc.
Par exemple, en PHP, on peut utiliser la fonction htmlentities()
pour encoder les caractères spéciaux :
<?php
if (isset($_GET['q'])) {
echo '<p>Search results for ' . htmlentities($_GET['q'], ENT_QUOTES, 'UTF-8') . '</p>';
}
?>
Ici on encode les caractères spéciaux en HTML, ce qui empêche l'exécution de code JavaScript injecté. On utilise aussi le paramètre ENT_QUOTES
pour encoder les guillemets simples et doubles, et UTF-8
pour s'assurer que l'encodage est correct.
Pour le navigateur, le code apparaîtra comme ceci, et il interprètera donc les caractères spéciaux comme du texte :
Exemple de sortie :
<p>Search results for <script>alert(1)</script></p>
Affichage dans le navigateur (comme du texte brut) :
Search results for <script>alert(1)</script>
Pour les XSS DOM-Based, il est recommandé d'utiliser des méthodes sécurisées pour manipuler le DOM, comme textContent
ou setAttribute
, qui n'interprètent pas le HTML. Notre exemple de code vulnérable ci-dessus peut être corrigé comme suit :
const q = new URLSearchParams(window.location.search).get("q");
if (q) {
const results = document.getElementById("results");
results.textContent = "Search results for: " + q;
}
Si vous avez tout de même besoin de conserver certaines balises HTML, vous pouvez utiliser des libraires comme DOMPurify, qui permettent de nettoyer le HTML tout en conservant certaines balises.
Vous pouvez également utiliser des iframe sandboxé qui permettent d’afficher du contenu HTML tout en bloquant l'exécution de JavaScript :
<iframe src="data:text/html;base64,<?= base64_encode($content) ?>" sandbox></iframe>
Pour en savoir plus, n'hésitez pas à consulter notre article sur le sujet.
Quand c'est possible, vous pouvez également utiliser des moteurs de templates sécurisés (comme Twig, Handlebars, etc.) qui gèrent l’échappement automatiquement et permettent d’éviter de nombreuses erreurs humaines.
Politique de sécurité de contenu (CSP)
Une Content Security Policy peut également être utilisée pour renforcer la sécurité au cas où, malgré votre vigilance, une XSS soit toujours présente sur votre site. Ce header HTTP permet de limiter les ressources autorisées à s’exécuter sur une page web. Cela réduit l’impact d’une éventuelle XSS :
Exemple de header CSP :
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none';
En savoir plus sur les CSP : https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP
Points d’entrée à surveiller
Les données pouvant être utilisées dans une XSS ne proviennent pas uniquement des champs GET ou POST. Elles peuvent aussi venir de :
- Cookies (utilisés sans décodage dans les pages)
- En-têtes HTTP (User-Agent, Referer, etc.)
- Attributs HTML construits dynamiquement
- Feuilles de style CSS injectées via des éditeurs WYSIWYG
- Variables insérées dans du JavaScript généré dynamiquement
Il est essentiel de vérifier et d’assainir toutes les entrées utilisateur, peu importe leur provenance.
Pour aller plus loin
En conclusion
Les XSS sont des failles critiques, fréquentes et dangereuses. Leur prévention repose sur une hygiène rigoureuse du code, un encodage systématique des données, et l'utilisation de fonctions sécurisées côté serveur comme côté client. Mettre en place une politique de sécurité de contenu (CSP), utiliser des templates sécurisés et respecter les principes de séparation des responsabilités dans le code sont des pratiques essentielles.
Besoin d’un audit de sécurité ou d’un accompagnement ? Secureaks propose des prestations en cybersécurité, incluant des pentests web et audits de code. Contactez-nous pour sécuriser vos applications.