Urgent : faille PHP critique. Comment vérifier →

Corriger votre Content Security Policy (CSP)

La Content Security Policy est l'en-tête de sécurité HTTP le plus puissant, et le plus souvent mal configuré. Si votre analyse de sécurité a signalé une CSP absente ou contenant unsafe-inline, ce guide explique le problème et comment le corriger correctement. Contrairement à la plupart des en-têtes, une CSP ne se corrige pas en copiant une valeur unique. La bonne politique dépend de ce que votre site charge réellement. Ce guide vous accompagne pour en construire une correctement.

Ce que signifie ce constat

Une Content Security Policy (CSP) est un en-tête de réponse HTTP qui indique au navigateur exactement quelles sources de scripts, de styles, d'images, de polices et de cadres peuvent être chargées sur votre page. Tout ce qui ne figure pas sur la liste approuvée est bloqué, ce qui empêche les attaques de cross-site scripting (XSS), dans lesquelles des scripts malveillants injectés s'exécutent dans le navigateur d'un visiteur.

mydomainrisk.com signale deux constats CSP :

  • Aucune CSP : votre site n'envoie aucun en-tête Content-Security-Policy. Le navigateur n'applique aucune restriction aux scripts ou ressources qui peuvent s'exécuter sur vos pages.
  • La CSP contient unsafe-inline : votre politique existe mais inclut 'unsafe-inline' dans la directive script-src ou style-src. Cela indique au navigateur d'autoriser tout script ou style intégré à la page : un attaquant capable d'injecter une seule balise <script> dans votre HTML peut alors exécuter du JavaScript arbitraire. Cela annule en grande partie l'intérêt d'une CSP.

Pourquoi c'est important

Le XSS figure toujours parmi les vulnérabilités web les plus exploitées. Une CSP bien configurée est la défense côté navigateur la plus efficace : elle limite ce qu'un attaquant peut faire même s'il parvient à injecter du code dans votre page. Sans elle, une attaque XSS réussie peut voler des cookies de session, rediriger les utilisateurs, exfiltrer des données ou défigurer votre site.

unsafe-inline est l'erreur de CSP la plus courante. Beaucoup de développeurs l'ajoutent pour éviter que leurs scripts intégrés ne cessent de fonctionner, au lieu de corriger le code sous-jacent, mais cela supprime la protection la plus importante qu'apporte la CSP.

Partie 1 : pourquoi on ne peut pas simplement copier une valeur de CSP

Contrairement à HSTS ou X-Content-Type-Options, il n'existe pas de valeur de CSP correcte universelle. Une politique qui fonctionne parfaitement pour un site HTML statique cassera une application React. Une politique qui fonctionne pour un CMS bloquera des ressources sur un autre.

Si vous copiez la CSP d'un guide ou d'un autre site pour l'appliquer au vôtre, l'une de ces deux situations se produira :

  • Elle est trop stricte et casse votre site (les images ne se chargent pas, les scripts échouent, les polices disparaissent)
  • Elle est trop permissive pour apporter une protection réelle

La bonne méthode consiste à observer d'abord, puis appliquer, en utilisant le mode report-only intégré à la CSP.

Partie 2 : construire votre CSP avec le mode report-only

Étape 1 : déployer d'abord une politique en mode report-only

Au lieu de Content-Security-Policy, utilisez Content-Security-Policy-Report-Only. Cet en-tête demande au navigateur d'évaluer votre politique et de signaler les violations, sans rien bloquer. Votre site continue de fonctionner normalement pendant que vous recueillez des données.

Commencez par une politique volontairement stricte en mode report-only :

Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-src 'none'; report-uri /csp-report

Étape 2 : mettre en place un point de collecte des rapports CSP (facultatif mais recommandé)

La directive report-uri envoie les rapports de violation sous forme de requêtes POST JSON vers un point de collecte que vous indiquez. Vous pouvez utiliser un service gratuit comme report-uri.com pour collecter et visualiser ces rapports sans créer votre propre point de collecte. Vous pouvez aussi ouvrir les outils de développement → Console et repérer les erreurs de violation CSP : elles indiquent précisément quelle ressource a été bloquée et depuis quelle source.

Étape 3 : parcourir votre site en détail

Une fois la politique report-only déployée, parcourez toutes les pages de votre site, y compris les espaces authentifiés, les parcours de paiement, les cartes intégrées, les widgets de discussion, les lecteurs vidéo et toutes les intégrations tierces. Chaque ressource bloquée générera un rapport de violation.

Étape 4 : interpréter les violations

Chaque violation indique quelle directive a été enfreinte (par exemple script-src) et quelle source a été bloquée (par exemple https://cdn.jsdelivr.net). Ajoutez chaque source légitime à la directive appropriée :

script-src 'self' https://cdn.jsdelivr.net;

Étape 5 : construire votre politique appliquée

Lorsque les violations ont cessé d'apparaître pour les ressources légitimes, votre politique est prête à être appliquée. Passez de Content-Security-Policy-Report-Only à Content-Security-Policy.

Partie 3 : supprimer unsafe-inline

Si votre site utilise actuellement unsafe-inline et que vous voulez le supprimer, la difficulté est que les scripts et styles intégrés ont besoin d'un autre mécanisme. Il existe deux options :

Option A : les nonces (recommandé pour les sites dynamiques)

Un nonce est un jeton aléatoire à usage unique, généré à chaque requête de page. Vous l'ajoutez à la fois dans l'en-tête CSP et dans chaque balise de script intégré.

Dans votre en-tête CSP :

Content-Security-Policy: script-src 'self' 'nonce-RANDOM_BASE64_VALUE'

Sur chaque balise de script intégré :

<script nonce="RANDOM_BASE64_VALUE">
  // your inline script
</script>

Le nonce doit être cryptographiquement aléatoire (au moins 128 bits), différent à chaque chargement de page et jamais réutilisé. La plupart des frameworks côté serveur (Next.js, Django, Laravel, Rails) prennent en charge les nonces nativement ou facilement.

Option B : les empreintes (recommandé pour les sites statiques)

Pour les scripts intégrés qui ne changent jamais, vous pouvez les autoriser par leur empreinte SHA-256 au lieu d'utiliser un nonce :

Content-Security-Policy: script-src 'self' 'sha256-BASE64_HASH_OF_SCRIPT_CONTENT'

Générez l'empreinte avec :

echo -n 'your inline script content here' | openssl dgst -sha256 -binary | openssl base64

Partie 4 : référence des directives CSP courantes

DirectiveContrôle
default-srcValeur de repli pour tous les types de ressources non listés explicitement
script-srcSources JavaScript
style-srcSources CSS
img-srcSources d'images
font-srcSources de polices web
connect-srcConnexions Fetch, XHR et WebSocket
frame-srcSources des iframes
object-srcContenu de plugins (Flash, etc.) : à définir sur 'none'
base-uriRestreint la balise <base> : à définir sur 'self'
form-actionDestinations autorisées pour l'envoi des formulaires

Valeurs sûres courantes : 'self' : même origine uniquement · 'none' : blocage total · https: : toute source HTTPS · data: : URI data (souvent nécessaires pour img-src)

Partie 5 : déployer l'en-tête selon votre plateforme

Cloudflare (Transform Rules)

Security → Transform Rules → Modify Response Header → Add rule → définissez Content-Security-Policy sur la valeur de votre politique.

Nginx

add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-src 'none'; object-src 'none'; base-uri 'self'; form-action 'self'" always;

Apache

Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-src 'none'; object-src 'none'; base-uri 'self'; form-action 'self'"

Vercel (vercel.json)

{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        {
          "key": "Content-Security-Policy",
          "value": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-src 'none'; object-src 'none'; base-uri 'self'; form-action 'self'"
        }
      ]
    }
  ]
}

Netlify (netlify.toml)

[[headers]]
  for = "/*"
  [headers.values]
    Content-Security-Policy = "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-src 'none'; object-src 'none'; base-uri 'self'; form-action 'self'"

Les exemples ci-dessus incluent 'unsafe-inline' dans style-src, car beaucoup de sites chargent des styles intégrés via des frameworks CSS. Supprimez-le si votre site n'utilise pas de styles intégrés, et remplacez-le par des nonces ou des empreintes.

Vérifier que cela fonctionne

Analysez votre domaine sur mydomainrisk.com : le constat CSP indiquera si votre politique est présente et si unsafe-inline a été retiré de script-src. L'analyse détecte aussi si votre CSP est en mode appliqué ou en mode report-only, et signale la différence. Si vous utilisez volontairement le mode report-only le temps d'affiner votre politique, ce n'est pas un problème : le constat le mentionnera au lieu de le traiter comme un échec.

Vous voyez encore unsafe-inline après votre correction ?

Vérifiez que :

  • votre en-tête est bien envoyé sur la bonne réponse (vérifiez dans les outils de développement → Network → cliquez sur votre page → Response Headers) ;
  • aucune CSP contradictoire n'est définie ailleurs (par exemple un CDN qui ajoute son propre en-tête) ;
  • vos nonces sont bien injectés dans vos balises de script intégré au moment du rendu.

Vérifiez votre Content Security Policy

MyDomainRisk détecte si votre CSP est présente, appliquée et exempte d'unsafe-inline, et l'intègre à l'évaluation globale du risque.

Analyser un domaine gratuitement →