Ce qui se passe
Gareth Heyes, chercheur chez PortSwigger, décrit comment une injection HTML vole des identifiants malgré une Content Security Policy qui interdit tout JavaScript. Rafał Wójcicki (AFINE) a publié la même technique de son côté. PortSwigger l'a intégrée à un skill de Burp AT, qui vérifie aussi un second point : si un domaine autorisé pour les scripts héberge un « gadget » connu — un point JSONP à callback contrôlable, une vieille copie d'AngularJS. La base en compte plus de 350, fournis par cspbypass.com.
Le mécanisme
Deux faits suffisent :
form-actionn'hérite pas dedefault-src. Une politique qui l'omet laisse les formulaires partir n'importe où.form-action 'self'autorise un formulaire qui se renvoie à la page elle-même, et l'attaque n'a besoin de rien d'autre.
Sur une page qui reflète un paramètre sans l'échapper, l'attaquant injecte un faux formulaire de connexion sans
attribut action. Il y ajoute une <textarea> qui porte le nom du paramètre vulnérable et contient la seconde
charge : une balise meta qui fixe la politique de referrer à unsafe-url, et une meta http-equiv="refresh"
vers son serveur.
À l'étape 3, le formulaire vise une origine autorisée par 'self'. À l'étape 4, la charge reflétée devient du HTML
actif. À l'étape 5, la redirection est une navigation, que la CSP ne filtre pas, et le navigateur envoie l'URL
complète — mot de passe compris — dans l'en-tête Referer. Aucune directive n'a été violée.
Le tester
PortSwigger maintient une page vulnérable exprès. Elle renvoie la politique
default-src 'none'; form-action 'self'; style-src 'nonce-…' et reflète le paramètre x sans échappement :
https://portswigger-labs.net/csp-form-action-test/?x=123Le délai de la redirection y est fixé à cinq secondes pour qu'on voie l'enchaînement.
Ce que ça change
- La correction, c'est l'échappement de ce que la page reflète. La CSP est un filet ; ici, il ne retient rien.
- Déclarez
form-actiondans chaque politique : son absence ouvre toutes les destinations, et'self'ne couvre pas ce cas. - En audit, partez de la directive réellement appliquée aux scripts —
script-src-elem, puisscript-src, puisdefault-src— et des hôtes autorisés : un seul fichier JSONP sur un domaine de la liste suffit. Avecstrict-dynamic, les navigateurs récents ignorent la liste d'hôtes ; avecunsafe-inlinesans nonce, hash nistrict-dynamic, aucun gadget n'est même nécessaire. - Plusieurs en-têtes CSP s'additionnent : un gadget ne passe que si toutes les politiques l'autorisent. Un
Content-Security-Policy-Report-Onlyseul ne bloque rien.
Source : PortSwigger Research, en anglais. Voir aussi l'article d'AFINE.