Une Content Security Policy bien construite transforme la plupart des failles d’injection de script en simples lignes de journal. Encore faut-il la déployer sans casser le site, ce qui suppose une méthode progressive plutôt qu’un en-tête copié depuis un exemple générique. Ce guide détaille les directives qui comptent, la façon de les mettre en place en observation avant de les rendre bloquantes, et les pièges propres aux sites riches en scripts tiers.
À quoi sert réellement une CSP
La Content Security Policy est un en-tête HTTP par lequel un serveur déclare au navigateur quelles sources de contenu sont légitimes pour une page. Le navigateur applique ensuite cette liste : un script chargé depuis un domaine non autorisé, un gestionnaire d’événement inline injecté par une attaque, ou un appel réseau vers un serveur pirate sont bloqués avant exécution.
Le bénéfice principal porte sur les attaques par injection de script, communément appelées XSS. Même lorsqu’un attaquant parvient à insérer une balise <script> dans une page, une politique stricte l’empêche de s’exécuter faute de correspondre à une source déclarée. La CSP couvre aussi la protection contre l’inclusion dans une iframe tierce, via la directive frame-ancestors, qui remplace l’ancien en-tête X-Frame-Options.
Les directives qui structurent une politique
Une politique se lit comme une suite de directives séparées par des points-virgules, chacune nommant un type de ressource et ses sources autorisées. La directive default-src sert de filet : elle s’applique à tout type non couvert par une directive plus spécifique.
| Directive | Contrôle | Valeur de départ raisonnable |
|---|---|---|
default-src | Filet pour les types non spécifiés | 'self' |
script-src | Origine des scripts JavaScript | 'self' + nonce |
style-src | Origine des feuilles de style | 'self' |
img-src | Origine des images | 'self' data: |
connect-src | Cibles des requêtes fetch, XHR, WebSocket | 'self' + API métier |
frame-ancestors | Qui peut embarquer la page en iframe | 'self' ou 'none' |
base-uri | Valeurs autorisées pour la balise base | 'self' |
form-action | Destinations de soumission des formulaires | 'self' |
object-src | Plugins hérités (Flash, applets) | 'none' |
Trois mots-clés reviennent en permanence. La valeur 'self' autorise la même origine que la page. La valeur 'none' interdit toute source. Les valeurs 'unsafe-inline' et 'unsafe-eval', à l’inverse, rouvrent la porte au script inline et à l’évaluation dynamique, et vident la politique d’une bonne partie de son intérêt dès qu’elles concernent script-src.
La méthode moderne : nonces et strict-dynamic
Autoriser une liste de domaines de confiance dans script-src fonctionne, mais cette approche vieillit mal : chaque nouveau service tiers allonge la liste, et un seul domaine autorisé compromis suffit à contourner la politique. L’approche recommandée aujourd’hui repose sur les nonces, des jetons aléatoires générés à chaque réponse et rattachés aux scripts légitimes.
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-r4nd0m2026' 'strict-dynamic';
style-src 'self';
img-src 'self' data:;
connect-src 'self' https://api.exemple.fr;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
object-src 'none';
upgrade-insecure-requests;Côté page, chaque script de confiance porte l’attribut correspondant, avec un jeton régénéré à chaque requête et jamais réutilisé.
<script nonce="r4nd0m2026" src="/assets/app.js"></script>Le mot-clé 'strict-dynamic' complète le dispositif : un script déjà autorisé par nonce peut charger à son tour d’autres scripts, sans que chaque dépendance ait besoin d’être listée. La politique se concentre alors sur la racine de confiance plutôt que sur une énumération de domaines qui dérive avec le temps.
Une liste de domaines autorisés grandit à chaque intégration ; un nonce, lui, ne fait confiance qu’au code que le serveur a réellement émis.
Déployer sans casser le site
Le risque d’une CSP n’est pas la faille, c’est la régression : une directive trop stricte fait disparaître une carte, un formulaire de paiement ou un traceur analytique. La parade tient en un en-tête distinct, Content-Security-Policy-Report-Only, qui applique exactement les mêmes règles mais sans rien bloquer. Le navigateur se contente de signaler ce qu’il aurait refusé.
Méthode en trois temps. Publier d’abord la politique en Report-Only et collecter les violations pendant plusieurs jours de trafic réel. Corriger ensuite les sources légitimes qui remontent (widgets, polices, API). Basculer enfin sur l’en-tête bloquant une fois le flux de rapports devenu silencieux.
La collecte des rapports passe par la directive report-to, adossée à l’API Reporting, qui envoie un document JSON à un point de terminaison à chaque violation. Pour une compatibilité plus large avec les navigateurs anciens, l’ancienne directive report-uri reste souvent maintenue en parallèle le temps de la transition.
Les pièges des sites riches en tiers
Un site vitrine servi par un serveur maîtrisé se prête bien aux nonces. Un site bâti sur un CMS extensible pose un problème différent : de nombreux greffons injectent leurs propres balises <script> inline, sans nonce, au moment du rendu. Sur WordPress, par exemple, la tentation de rétablir 'unsafe-inline' pour faire taire les erreurs revient à désactiver la protection sur le point le plus sensible.
Trois réflexes limitent la casse. D’abord, inventorier les scripts tiers réellement utilisés et retirer ceux qui ne servent plus. Ensuite, préférer les extensions qui permettent d’attacher un nonce, ou déporter les scripts inline vers des fichiers servis depuis la même origine. Enfin, traiter connect-src avec autant de soin que script-src : c’est la directive qui empêche l’exfiltration de données vers un serveur tiers, souvent l’objectif final d’une injection réussie.
Tester et maintenir la politique dans le temps
Une CSP n’est jamais figée : chaque nouvelle fonctionnalité, chaque intégration marketing, chaque mise à jour d’extension peut introduire une source légitime que la politique ignore. Sans discipline de maintenance, deux dérives guettent. La première voit la liste des sources autorisées gonfler au fil des demandes urgentes, jusqu’à autoriser large et vider la protection de son sens. La seconde voit la politique bloquer silencieusement une fonctionnalité que personne ne teste, jusqu’à ce qu’un utilisateur signale le dysfonctionnement.
La console du navigateur reste le premier outil de diagnostic : chaque violation y apparaît avec la directive fautive et la ressource refusée, ce qui suffit à identifier une régression pendant le développement. Pour une évaluation plus systématique, des analyseurs en ligne notent une politique et pointent les mots-clés qui l’affaiblissent, au premier rang desquels 'unsafe-inline' et les jokers trop larges. Intégrer cette vérification à la chaîne d’intégration continue évite qu’une politique se dégrade sans que personne ne s’en aperçoive.
La maintenance gagne à être cadrée par quelques règles simples : documenter la raison de chaque source autorisée, revoir la politique à chaque ajout de service tiers, et conserver l’endpoint de rapport actif même après le passage en mode bloquant. Les violations qui continuent d’arriver en production signalent soit une attaque en cours, soit une fonctionnalité légitime oubliée lors du durcissement. Dans les deux cas, l’information mérite d’être vue.
Ce qu’il faut retenir
- La CSP neutralise l’exécution des scripts injectés, à condition d’éviter
'unsafe-inline'surscript-src. - Les nonces associés à
'strict-dynamic'remplacent avantageusement les listes de domaines. - Le déploiement se fait en
Report-Onlyd’abord, puis en mode bloquant une fois les faux positifs traités. frame-ancestors,base-uri,form-actionetobject-src 'none'complètent la protection au-delà des seuls scripts.
La première fois que j’ai déployé une CSP directement en mode bloquant, j’ai cassé le tunnel de paiement d’un client en pleine journée. Depuis, je ne démarre plus jamais autrement qu’en Report-Only, et je laisse tourner une semaine complète avant de serrer la vis. Sur les sites WordPress chargés d’extensions, je considère qu’une CSP à moitié stricte mais réellement en place vaut mieux qu’une politique parfaite sur le papier que personne n’ose activer. — Simon Janvier
Pour aller plus loin
Référence des directives et de leur prise en charge : Content-Security-Policy sur MDN.
