WAF et filtres XSS : ce qui casse en production
Les filtres XSS génériques bloquent parfois des tests simples, puis ratent le vrai contexte vulnérable. En production, le coût opérationnel arrive vite.
Un WAF peut aider. Un filtre XSS global peut aussi créer une dette bizarre, difficile à diagnostiquer et très bruyante.
Le problème n'est pas que les WAF seraient inutiles. Le problème, c'est l'attente qu'on met dessus. Beaucoup d'équipes espèrent qu'une règle générique va comprendre le contexte applicatif mieux que l'application elle-même. Elle ne le fera pas. Elle voit une requête, des paramètres, parfois un corps JSON, quelques en-têtes, et applique des heuristiques.
Le navigateur, lui, interprète un DOM final.
Le filtre ne voit pas toujours le bon moment
Une XSS dépend souvent d'une chaîne de transformations. La requête arrive encodée, le framework décode, l'application normalise, le template échappe partiellement, le frontend réinjecte une valeur, puis le navigateur parse le résultat. Un WAF placé devant l'application ne voit qu'une partie du film.
Il peut bloquer une chaîne très visible et laisser passer un flux réellement dangereux parce que le danger apparaît après transformation. Il peut aussi bloquer une valeur parfaitement légitime parce qu'un utilisateur a écrit du HTML dans un champ de documentation, un ticket support ou un CMS interne.
Le faux positif n'est pas un détail. En production, il devient un incident.
Les règles trop larges coûtent cher
Une règle agressive peut casser :
- des recherches contenant des caractères spéciaux ;
- des exports CSV avec fragments HTML ;
- des intégrations Markdown ;
- des champs de configuration qui stockent des templates ;
- des clients API qui encodent différemment selon leur langage.
Quand ça arrive, l'équipe finit par ajouter une exception. Puis une autre. Six mois plus tard, personne ne sait quelles routes sont vraiment protégées.
Comment tester sans jouer au chat et à la souris
L'objectif d'un outil comme xsspayloads n'est pas de "battre le WAF" pour le sport. Dans un cadre autorisé, il sert à comprendre où la protection agit et ce qu'elle masque.
Une note d'audit utile ressemble à ceci :
Route : POST /profile
Champ : displayName
Observation : blocage WAF sur certaines formes avant application
DOM final : valeur encore insérée dans un attribut après normalisation
Risque : protection dépendante d'une règle périmétrique, pas d'un encodage de sortie
Cette différence change le correctif. On ne demande pas seulement de modifier une règle. On demande de supprimer la possibilité pour une donnée non fiable d'atteindre un contexte dangereux.
La défense durable est locale au contexte
Les contrôles périmétriques sont utiles pour réduire le bruit, absorber des attaques opportunistes et gagner du temps. Ils ne remplacent pas le code correct.
Une défense durable vit près du rendu : moteur de template qui échappe par défaut, API DOM sûres, sanitization stricte quand du HTML utilisateur est vraiment nécessaire, CSP pour limiter l'impact, tests de régression sur les flux sensibles.
Le WAF est une couche. Pas un alibi.