En bref : vous prévenez le XSS en PHP en encodant chaque sortie selon son contexte : htmlspecialchars() pour le HTML, json_encode() avec options de protection pour JavaScript, URL vérifiées pour les liens. Les templates à échappement automatique vous en épargnent une grande partie. Une Content Security Policy et des cookies HttpOnly limitent les dégâts si une faille subsiste malgré tout.
Pourquoi le cross-site scripting devrait être contrôlé en même temps
En octobre 2026, l'attention se porte sur PHP2Shell, deux failles critiques de l'interpréteur PHP pour lesquelles des correctifs existent depuis le 21 octobre 2026. De nombreuses entreprises font vérifier leurs applications web à cette occasion. C'est un bon moment pour examiner aussi les vulnérabilités du code propre qu'aucune mise à jour de PHP ne corrige.
Le cross-site scripting, XSS en abrégé, en est une. L'OWASP Top 10 classe le XSS dans la catégorie Injection. Contrairement à une faille du serveur, l'attaque vise ici les visiteurs et utilisateurs de votre site, par exemple des clients dans la boutique ou des collaborateurs dans l'administration.
Les conséquences peuvent être considérables : sessions d'administrateurs détournées, formulaires manipulés qui envoient des données à des tiers ou faux contenus sur votre site. Si un compte administrateur est repris, cela peut aller jusqu'au contrôle total de l'application.
Comment le XSS apparaît-il dans les applications PHP ?
Le XSS apparaît lorsqu'une application affiche dans une page, sans les vérifier, des données provenant d'une source non fiable. Le navigateur ne peut alors plus distinguer vos contenus du code introduit et exécute les deux.
On distingue trois formes. Dans le XSS réfléchi, le contenu provient directement de la requête en cours, par exemple d'un terme de recherche dans l'URL. Dans le XSS stocké, il se trouve dans la base de données, par exemple dans un commentaire ou un champ de profil, et touche toute personne qui consulte la page. Le XSS basé sur le DOM naît dans le JavaScript du navigateur, lorsque des scripts écrivent dans la page des valeurs provenant de l'URL.
Sont non fiables toutes les données qui ne figurent pas en dur dans le code : champs de formulaire, paramètres d'URL, cookies, noms de fichiers, réponses d'interfaces externes et contenus de votre propre base de données qui, à un moment donné, provenaient de l'extérieur.
Encoder correctement la sortie selon le contexte
La règle la plus importante : encoder à la sortie, et adapter au lieu où la valeur atterrit. Pour le contenu HTML et les attributs entre guillemets, htmlspecialchars() est le moyen de choix. Une petite fonction utilitaire évite d'oublier des options :
<?php
function e(?string $wert): string
{
return htmlspecialchars($wert ?? '', ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5, 'UTF-8');
}
?>
<h2><?= e($produkt['name']) ?></h2>
<input type="text" name="suche" value="<?= e($_GET['q'] ?? '') ?>">
D'autres contextes exigent d'autres moyens :
| Contexte | Méthode sûre |
|---|---|
| Contenu HTML, attribut entre guillemets | htmlspecialchars() avec ENT_QUOTES et UTF-8 |
| Données pour JavaScript | json_encode() avec JSON_HEX_TAG, JSON_HEX_AMP, JSON_HEX_APOS, JSON_HEX_QUOT |
| Paramètre dans une URL | rawurlencode() pour la valeur isolée |
| URL complète issue d'une saisie utilisateur | Vérifier le schéma, n'autoriser que https et http, puis htmlspecialchars() |
| Attributs sans guillemets, gestionnaires d'événements, CSS | éviter, ne pas y insérer de valeurs |
Pour les données dont un script a besoin, un attribut de données ou un bloc JSON est généralement la voie la plus propre :
<?php
$daten = json_encode($konfiguration, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT);
?>
<div id="app" data-konfig="<?= e($daten) ?>"></div>
En JavaScript, vous lisez ensuite la valeur via l'attribut de données et vous insérez les contenus avec textContent plutôt qu'avec innerHTML.
Templates, nettoyage du HTML et frameworks
Les moteurs de templates comme Twig et Blade encodent les sorties automatiquement par défaut. Cela exclut une grande partie des erreurs typiques. Cela devient dangereux là où ce comportement est volontairement désactivé, par exemple avec le filtre raw dans Twig ou la syntaxe {!! !!} dans Blade. De tels endroits devraient être rares dans le code et vérifiés spécifiquement lors de chaque revue.
Si votre application doit volontairement autoriser du HTML venant des utilisateurs, par exemple des mises en forme dans les commentaires ou dans un système éditorial, l'encodage ne suffit pas, puisque le HTML doit être affiché. Il vous faut alors une bibliothèque de nettoyage éprouvée comme HTML Purifier, qui ne laisse passer qu'une liste fixe d'éléments et d'attributs autorisés. Les filtres maison à base d'expressions régulières sont, d'expérience, lacunaires.
Contrôlez tout particulièrement les zones d'administration. Le XSS dans des vues que seuls les administrateurs voient est souvent sous-estimé, mais touche précisément les comptes aux droits les plus étendus.
Trouver le XSS dans le code PHP existant
Dans les applications qui ont grandi, la première question n'est généralement pas de savoir comment bien encoder, mais où cela a été oublié jusqu'ici. Un point de départ pragmatique est la recherche des endroits où des variables sont affichées directement :
# Sorties directes de données de requête
grep -rnE "(echo|print|<\?=)\s*\\\$_(GET|POST|REQUEST|COOKIE|SERVER)" src/ templates/ --include="*.php"
# Échappement automatique désactivé dans Twig et Blade
grep -rnE "\|raw|\{!!" templates/ resources/views/
Les résultats sont des tâches de vérification, pas des constats définitifs. Une telle recherche ne trouve le XSS stocké que de façon limitée, car les valeurs y proviennent de la base de données. Contrôlez donc en plus toutes les vues qui affichent des contenus d'utilisateurs : commentaires, profils, notes de commande, tickets de support. Pour les CMS et les boutiques, les plugins et thèmes de tiers en font expressément partie, car une grande partie de la logique d'affichage s'y trouve.
Deuxième ligne de défense : CSP et cookies sûrs
Même avec un code soigné, un endroit peut être oublié. Deux mesures limitent alors les dégâts. Une Content Security Policy n'autorise dans le navigateur que des scripts provenant de sources définies et interdit les scripts intégrés, sauf s'ils sont expressément autorisés :
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'"
Introduisez une telle politique progressivement, d'abord en mode purement rapport via Content-Security-Policy-Report-Only, car les intégrations existantes de services d'analyse ou de paiement doivent le plus souvent être ajoutées. Le guide En-têtes de sécurité pour les sites web décrit d'autres en-têtes.
En outre, les cookies de session devraient porter l'attribut HttpOnly afin que les scripts ne puissent pas les lire. Le guide Sécuriser les sessions en PHP explique comment sécuriser les sessions dans leur ensemble. Une vulnérabilité voisine côté base de données est traitée dans Prévenir l'injection SQL en PHP.
Comment PHP SOS aide-t-il concrètement ?
- Nous examinons les templates et les sorties de votre application à la recherche d'encodages manquants ou erronés.
- Nous évaluons les constats selon l'accessibilité et les comptes concernés et montrons des corrections concrètes.
- Nous introduisons progressivement une Content Security Policy, sans perturber les fonctions de boutique ou de paiement.
- Nous retestons après la correction et documentons le résultat.
La prestation Audit de sécurité et test d'intrusion décrit un contrôle indépendant.
Questions fréquentes
htmlspecialchars suffit-il contre le XSS ?
Pour du texte normal dans le contenu HTML et dans des attributs entre guillemets, oui, si vous indiquez ENT_QUOTES et le jeu de caractères UTF-8. Dans JavaScript, dans les URL ou dans le CSS, d'autres règles s'appliquent ; il faut y recourir à un encodage propre et adapté.
Dois-je filtrer les entrées lors de l'enregistrement ou à l'affichage ?
Encodez à l'affichage, en fonction de la destination. Les entrées doivent être contrôlées à la réception pour leur plausibilité, par exemple longueur et format. Qui ne filtre qu'à l'enregistrement oublie les données provenant d'autres sources et complique l'utilisation ultérieure dans d'autres contextes.
À quoi sert une Content Security Policy ?
Une Content Security Policy détermine de quelles sources le navigateur peut charger et exécuter des scripts. Elle peut nettement limiter les effets d'une faille XSS passée inaperçue, mais ne remplace pas un encodage correct.
Comment autoriser des mises en forme comme le gras ou des liens dans les commentaires ?
Utilisez une bibliothèque de nettoyage du HTML éprouvée avec une liste blanche d'éléments et d'attributs autorisés. Des expressions régulières maison pour supprimer des scripts sont sujettes aux erreurs et ne doivent pas être utilisées.