Aller au contenu

Le média des artisans du web dimanche 30 août 2026

Sécurité

Content-Security-Policy : bâtir une politique qui protège sans tout casser

La Content-Security-Policy est un en-tête HTTP qui limite l'impact d'une injection de script en filtrant les sources autorisées. Ce guide déroule un déploiement progressif : directives clés, nonces plutôt qu'unsafe-inline, et mode Report-Only pour ne rien casser en production.

La Content-Security-Policy (CSP) est un en-tête de réponse HTTP qui indique au navigateur quelles sources de contenu il a le droit de charger et d’exécuter. Bien posée, elle transforme une faille d’injection de script en simple entrée bloquée dans la console. Mal comprise, elle se résume à une ligne recopiée qui n’arrête rien ou qui casse la moitié du site. Ce guide décrit un déploiement progressif, des directives au mode de test, pour un socle qui protège sans paralyser la production.

Ce que verrouille une Content-Security-Policy

Le cross-site scripting (XSS) reste l’une des vulnérabilités web les plus répandues : un attaquant parvient à injecter du JavaScript dans une page, et ce code s’exécute avec les droits de la victime. La CSP agit en défense en profondeur. Même si une injection passe la validation d’entrée, le navigateur refuse d’exécuter un script dont l’origine n’est pas explicitement autorisée par la politique. L’en-tête ne remplace pas l’échappement des données, il ajoute une seconde barrière qui limite fortement l’impact d’une erreur applicative.

Anatomie de l’en-tête : les directives à connaître

Une politique est une liste de directives séparées par des points-virgules. Chaque directive nomme un type de ressource et la liste des sources autorisées, exprimées en mots-clés ('self', 'none') ou en domaines.

Content-Security-Policy: default-src 'self'; img-src 'self' data:; style-src 'self'; connect-src 'self' https://api.exemple.com; object-src 'none'
DirectiveRôle
default-srcValeur de repli pour les directives non précisées
script-srcOrigines autorisées pour le JavaScript
style-srcOrigines autorisées pour les feuilles de style
img-srcOrigines des images (souvent 'self' data:)
connect-srcCibles de fetch, XHR, WebSocket, EventSource
frame-ancestorsQui peut inclure la page dans une iframe (anti-clickjacking)
base-uriRestreint la balise <base>, souvent 'self'
object-srcPlugins hérités, à mettre à 'none'

La directive frame-ancestors mérite une mention à part : elle remplace l’ancien en-tête X-Frame-Options et contrôle qui peut afficher la page dans un cadre, ce qui coupe court aux attaques de type clickjacking.

Le piège de ‘unsafe-inline’ : nonces et empreintes

La tentation la plus courante consiste à ajouter 'unsafe-inline' à script-src pour que les scripts embarqués dans le HTML continuent de fonctionner. Ce mot-clé annule l’essentiel de la protection : il autorise justement le type de script qu’un attaquant cherche à injecter. Deux mécanismes permettent d’autoriser un script inline précis sans ouvrir la porte à tous les autres. Le nonce est une valeur aléatoire, régénérée à chaque réponse, posée à la fois dans l’en-tête et sur la balise.

<!-- En-tête : script-src 'nonce-r4Nd0mBase64' 'strict-dynamic' -->
<script nonce="r4Nd0mBase64">
  // ce bloc précis est autorisé, aucun autre inline ne l'est
</script>

L’empreinte ('sha256-…') suit la même logique pour un script au contenu figé. Le mot-clé 'strict-dynamic' complète le dispositif : la confiance accordée à un script muni d’un nonce se propage aux scripts qu’il charge, ce qui évite d’entretenir une longue liste de domaines.

Ajouter ‘unsafe-inline’ à script-src revient à autoriser précisément le type de code qu’une attaque XSS cherche à injecter.

Déployer sans casser la production : le mode Report-Only

Activer une politique stricte d’un coup sur un site existant provoque presque toujours des ruptures : une balise de suivi, une police externe, un widget tiers se retrouvent bloqués. L’en-tête Content-Security-Policy-Report-Only résout ce problème. Il applique la politique en observation : le navigateur ne bloque rien, mais signale chaque violation à un point de collecte. La liste des ressources refusées permet d’ajuster la politique avant de la faire respecter pour de bon.

add_header Content-Security-Policy-Report-Only "default-src 'self'; report-uri /csp-report" always;

Point de vigilance : commencez toujours par Report-Only sur un site en production, laissez tourner plusieurs jours pour couvrir tous les parcours, puis basculez sur l’en-tête bloquant. Vérifiez aussi que le proxy ou le CDN ne réécrit pas l’en-tête, sous peine d’appliquer une politique différente de celle prévue.

Collecter et lire les rapports de violation

Le mécanisme historique, report-uri, demande au navigateur d’envoyer un document JSON à chaque violation, décrivant la page concernée, la directive enfreinte et la ressource bloquée. L’approche moderne associe l’en-tête Reporting-Endpoints à la directive report-to, mieux intégrée à l’API de reporting du navigateur. Une route serveur légère suffit à recueillir ces envois.

{
  "csp-report": {
    "document-uri": "https://exemple.com/compte",
    "violated-directive": "script-src 'self'",
    "blocked-uri": "https://cdn.tiers.com/widget.js"
  }
}

La lecture régulière de ces rapports met au jour les sources tierces à autoriser explicitement : une police servie depuis un CDN, une carte ou une vidéo intégrée, un script de mesure d’audience. Chacune devient une ligne assumée de la politique, jamais un blanc-seing accordé à toute une catégorie.

Un socle de départ raisonnable

Pour un site classique servi par Nginx, une politique restrictive mais fonctionnelle peut servir de base, à élargir directive par directive selon les remontées du mode observation.

add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; object-src 'none'; base-uri 'self'; frame-ancestors 'self'; form-action 'self'; upgrade-insecure-requests" always;

La directive upgrade-insecure-requests demande au navigateur de convertir les requêtes HTTP résiduelles en HTTPS, un filet utile lors d’une migration. Chaque ajout de source doit répondre à un besoin identifié, jamais à un blocage réglé par facilité avec 'unsafe-inline'.

Ce qu’il faut retenir

La CSP est une couche de défense qui limite l’impact d’une injection quand tout le reste a échoué. La méthode qui tient repose sur trois principes : bannir 'unsafe-inline' au profit des nonces ou des empreintes, déployer d’abord en Report-Only pour cartographier les ressources légitimes, puis resserrer directive par directive. Une politique posée dans cet ordre protège durablement sans transformer chaque mise en production en chasse aux régressions.

Sur les sites que je maintiens, je pose toujours la CSP en Report-Only pendant au moins une semaine avant de la rendre bloquante, et je centralise les rapports pour les relire à froid. C’est fastidieux, mais c’est la seule façon que j’ai trouvée d’éviter le scénario classique : une politique stricte activée un vendredi soir, et le formulaire de contact qui ne répond plus le lundi. La CSP n’est pas un interrupteur, c’est un réglage progressif. — Simon Janvier

Pour aller plus loin

Référence des directives et de leur syntaxe : Content-Security-Policy sur MDN Web Docs.

À lire aussi sur Mail Studio

Partager LinkedIn Bluesky Hacker News E-mail

À lire aussi