Politique de sécurité du contenu : configuration CSP, risques de contournement et déploiement
1. Ce que la CSP bloque réellement
La CSP diffuse ses règles via un en-tête de réponse HTTP, et le navigateur décide en fonction quelles ressources peuvent se charger. Elle protège principalement contre deux problèmes : les scripts malveillants injectés par XSS et l’intégration de domaines tiers non autorisés dans la page.
Ce n’est pas un pare-feu. La CSP ne bloque ni les injections SQL ni la compromission du serveur ; elle gère le chargement des ressources côté navigateur. Elle fonctionne en complément de HTTPS et HSTS. Pour configurer HTTPS, voir Configurer HTTPS sur un site indépendant.
Le problème courant des sites indépendants, c’est le trop grand nombre de scripts tiers : GA4, GTM, Hotjar, chat en ligne, pixels publicitaires. Chaque script ajouté exige d’ajouter un domaine à la liste blanche CSP, sinon le navigateur le bloque. Pour gérer les scripts tiers, voir Gérer les scripts tiers sur un site indépendant.
Un fait souvent négligé : la CSP n’a quasiment aucun impact négatif sur le SEO. Googlebot n’applique pas de restriction sur les ressources autres que JS, et une CSP bien configurée n’affecte pas l’indexation. Craindre que la CSP nuise au crawl est inutile.
| Directive | Ce qu’elle gère | Valeur conseillée |
|---|---|---|
| default-src | Toutes les ressources par défaut | ‘self’ |
| script-src | Sources des scripts JS | ‘self’ + domaines de statistiques |
| style-src | Sources CSS | ‘self’ ‘unsafe-inline’ |
| img-src | Sources des images | ‘self’ data: + CDN |
| object-src | Extension | ‘none’ (obligatoire) |
| frame-ancestors | Qui peut intégrer le site | ‘none’ |
2. Déploiement en trois étapes : rapport, affinage, application
L’erreur la plus fréquente est d’activer directement l’en-tête Content-Security-Policy. Cela bloque immédiatement tous les scripts du site non inscrits sur la liste blanche : GA4, chat en ligne, pixels tombent tous en panne, et les équipes métier se plaignent aussitôt.
La bonne méthode consiste à commencer par l’en-tête Content-Security-Policy-Report-Only. Le navigateur signale les violations sans les bloquer ; vous collectez les journaux pendant deux semaines et ajoutez à la liste blanche tous les domaines réellement utilisés.
Exemple de scénario 1 : ajouter une CSP à un site WordPress, en commençant par Report-Only.
Deux jours plus tard, le rapport fait apparaître trois domaines : googletagmanager.com, hotjar.com et youtube.com.
Ajoutez-les un par un dans script-src et frame-src. Au bout de deux semaines, le rapport ne signale plus que quelques cas par jour ; passez alors en mode application.
| Phase | En-tête de réponse | Action | Durée |
|---|---|---|---|
| Première étape | Content-Security-Policy-Report-Only | Signaler sans bloquer | 14 jours |
| Deuxième étape | Idem, ajouter progressivement la liste blanche | Les violations diminuent chaque jour | Jusqu’à un chiffre |
| Troisième étape | Content-Security-Policy | Blocage réel | Long terme |
| En permanence | Ajouter report-to ou report-uri | Collecter les signalements de violation | Toujours actif |
Les scripts inline sont le plus gros piège. Les thèmes WordPress et beaucoup d’extensions écrivent directement des <script> dans la page, et CSP bloque les inline par défaut. Deux solutions : soit extraire les scripts inline dans un fichier js externe, soit autoriser ce passage précis avec un hash sha256, plutôt que d’activer unsafe-inline par facilité.
style-src est plus permissif que script-src. L’éditeur Gutenberg et beaucoup de thèmes génèrent des styles inline, garder temporairement unsafe-inline sur style-src ne pose pas de gros problème.
Mais script-src ne doit jamais activer unsafe-inline, c’est la ligne de défense centrale de CSP contre le XSS. Il faut traiter différemment la tolérance de ces deux directives.
3. Risques de contournement : ne laissez pas CSP être une coquille vide
Un CSP mal configuré revient à ne pas en avoir. Les trois écritures suivantes rendent ce mur totalement inopérant.
La première consiste à activer unsafe-inline. Elle autorise l’exécution de n’importe quel script inline, y compris le script malveillant injecté par XSS, et la ligne de défense centrale de CSP tombe. La deuxième consiste à activer le joker *, ce qui revient à ne limiter aucune source. La troisième oublie object-src, et les anciens navigateurs peuvent encore contourner via Flash ou des extensions.
Exemple de scénario 2 : par facilité, on écrit ‘unsafe-inline’ dans script-src.
Résultat : deux mois plus tard, le site se fait injecter un script de phishing inline, et CSP ne bloque rien du tout.
La bonne méthode est d’utiliser un nonce, un nombre aléatoire à usage unique généré à chaque requête, pour n’autoriser que les scripts inline portant ce nombre.
| Écriture erronée | Risque | Alternative |
|---|---|---|
| script-src * | N’importe quel script tiers peut se charger | Lister les domaines un par un en liste blanche |
| script-src ‘unsafe-inline’ | Le script injecté par XSS s’exécute normalement | Extraire dans un fichier externe ou utiliser nonce/hash |
| Ne pas écrire object-src | Les vieilles extensions peuvent être détournées | object-src ‘none’ |
| Ne pas configurer frame-ancestors | La page est imbriquée dans une iframe malveillante | frame-ancestors ‘none’ |
CSP et conformité sont deux choses distinctes. Le consentement aux cookies du RGPD est un autre mécanisme, voir Conformité RGPD pour un site indépendant. CSP protège contre l’injection de scripts, il ne règle pas la question du consentement aux cookies. Pour la connexion et les permissions de fichiers au niveau serveur, voir Renforcement de la sécurité d’un site indépendant.
Comment choisir entre nonce et hash ? Pour un script inline fixe, utilisez un hash ; pour un script inline généré dynamiquement, utilisez un nonce. Dans un thème WordPress, le bout de code de suivi fixe se valide par hash ; les blocs de script dont le contenu change à chaque requête utilisent un nonce.
L’emplacement de déploiement compte aussi. Il vaut mieux envoyer l’en-tête de réponse CSP au niveau Nginx ou CDN, plutôt que de le faire produire par l’application PHP. Une sortie au niveau applicatif disparaît en cas de panne PHP, alors qu’un envoi au niveau CDN reste stable.
| Emplacement de déploiement | Stabilité | Cas d’usage |
|---|---|---|
| Configuration Nginx | Élevée, reste en place même si l’application tombe | Serveur auto-hébergé |
| Règles CDN | Élevée, effective dans le monde entier | Si vous utilisez Cloudflare, etc. |
| Sortie de l’application PHP | Faible, perdue en cas de panne | Test temporaire |
Le point de réception des rapports doit aussi être créé spécifiquement. L’adresse vers laquelle pointe report-uri ne doit pas être bloquée par CSP lui-même, sinon les rapports ne partent pas.
Pour un site autonome, vous pouvez utiliser un service de collecte de rapports existant, ou configurer un point de terminaison de journal dédié dans Nginx pour recevoir les signalements, écrire dans un fichier de journal séparé, sans passer par le journal de trafic du site principal.
Une fois le volume de rapports important, il faut agréger. Vous pouvez recevoir plusieurs centaines de signalements par jour, impossible de tout examiner un par un. Regroupez les statistiques par directive violée et par domaine bloqué, en surveillant surtout les nouveaux domaines ajoutés chaque jour.
Une idée fausse courante : croire qu’une fois le CSP en place, tout est réglé. En réalité, chaque ajout d’un nouveau script tiers, chaque mise à jour du thème, peut introduire une nouvelle source de violation.
Donc après le passage en Enforce, report-uri ne doit pas être désactivé. C’est votre seule source de signalement pour détecter « un script ajouté qu’on a oublié de mettre en liste blanche ».
Un mot aussi sur la compatibilité des navigateurs. Les navigateurs modernes (Chrome, Edge, Firefox, Safari des deux dernières années) prennent tous en charge CSP 3 de manière complète. Les vieux IE peuvent être ignorés, les visiteurs d’un site autonome B2B n’utilisent pratiquement pas IE.
Autre détail de déploiement : l’en-tête CSP doit être envoyé à la fois en http et en https. Si vous ne l’envoyez qu’en https, les requêtes en phase de retour à l’origine en http ne sont pas protégées par le CSP.
Après le passage en Enforce, il est conseillé de conserver en parallèle un en-tête Report-Only pendant un certain temps : à la fois blocage et signalement, et si quelque chose a été oublié, vous pourrez le voir dans les rapports.
Le CSP n’est pas un projet ponctuel. À chaque ajout d’un nouveau script tiers (support client, chat, vidéo, publicité), il faut d’abord observer en mode rapport qu’il apparaît dans les violations, puis ajouter le domaine correspondant à la liste blanche, et seulement ensuite passer en production.
Ajoutez la vérification CSP à votre liste de contrôle avant la mise en ligne d’un nouveau script : ajoutez le script et la liste blanche en même temps, n’attendez pas qu’après la mise en ligne un utilisateur signale « le composant ne s’affiche pas » pour penser au CSP.
Un critère de décision : si un script tiers ne peut être transformé en fichier externe, ni faire l’objet d’un calcul de hachage, il faut réévaluer si ce tiers vaut la peine d’être utilisé. Un service tiers qui dépend de unsafe-inline est en soi une dette technique.
Une fois le déploiement terminé, vérifiez page par page avec le panneau Security de la console du navigateur. Passez en revue la page d’accueil, la page produit, la page blog et la page formulaire, pour confirmer qu’aucune alerte de violation rouge n’apparaît.
Pour les pages qui échouent à la vérification, retournez dans les rapports chercher le domaine correspondant, ajoutez-le à la liste blanche et observez encore un jour. Ce cycle répété deux ou trois fois suffit à stabiliser.
Le CSP n’est pas un élément de sécurité qui se termine après un déploiement : il évolue sur le long terme avec votre bibliothèque de scripts tiers. À chaque ajout d’un nouveau script, il faut mettre à jour la liste blanche en même temps, sans jamais oublier de configurer le domaine d’un nouveau script tiers qui apparaît.
| Source du rapport | Signification | Traitement |
|---|---|---|
| Violation script-src | Nouveau script sans liste blanche | Ajouter le domaine ou extraire le fichier externe |
| Violation img-src | Image liée depuis un nouveau domaine | Ajouter le CDN ou passer en chemin relatif |
| Violation frame-src | Nouvelle iframe intégrée | Ajouter le domaine vidéo ou le retirer |
| Violation base-uri | Balise base injectée dans la page | Rechercher immédiatement un XSS |
FAQ — Questions fréquentes
Faut-il activer le CSP en mode forcé dès la mise en ligne ?
Que faire quand un script inline d’un thème WordPress est bloqué par la CSP ?
Quel est le lien entre la CSP, HTTPS et HSTS ?
Pourquoi mes rapports CSP ne contiennent-ils que des violations ?
La CSP peut-elle bloquer les injections SQL ?
Faut-il encore garder report-uri ?
Pour conclure
La difficulté de la CSP n’est pas d’écrire les règles, mais d’avoir la patience de faire tourner le mode rapport. Deux semaines d’observation pour des années de scripts maîtrisés : le calcul est vite fait.
Trois étapes pour passer à l’action : d’abord lancer Report-Only pendant 14 jours ; ajouter une à une à la liste blanche les domaines réels d’après les rapports et remplacer unsafe-inline par des hachages ; une fois les violations tombées à quelques unités, passer en mode forcé tout en gardant le rapport pour surveiller en continu.
Une phrase à retenir : le niveau de sécurité d’une CSP dépend de la propreté de sa liste blanche. Garder unsafe-inline et des jokers, c’est un mur qui n’a jamais été monté.
