csp

CSP outils

Content-Security-Policy en pratique : ce que gouverne chaque directive, comment les nonces, les empreintes et les expressions de source correspondent, et comment déployer une politique avec Report-Only.

1 outils

§01 GUIDE DU DOMAINE

Ce qu’est réellement une politique

Content-Security-Policy est un en-tête de réponse composé de directives, chacune contenant une liste d’expressions de source. Avant que le navigateur ne charge une sous-ressource, n’exécute du code en ligne, ne soumette un formulaire ou ne laisse la page être encadrée, il consulte la directive compétente ; si rien ne correspond, la requête n’a jamais lieu et une violation est rapportée. L’application se fait dans le navigateur, et une politique ne retire que des capacités, elle n’en accorde jamais.

La correspondance travaille sur les origines — schéma, hôte, port — et non sur l’URL que vous aviez en tête : 'self' sur https://app.example.com ne couvre ni https://cdn.example.com ni ses sous-domaines. Une politique ne vaut par ailleurs que son échappatoire la plus faible : script-src 'self' 'unsafe-inline' livre à quiconque peut injecter du balisage une balise de script fonctionnelle — l’attaque même que la CSP existe pour arrêter. Remplacez 'unsafe-inline' par un nonce par réponse ou une empreinte de contenu.

Les politiques se composent par intersection, jamais par union : quand une réponse porte deux en-têtes Content-Security-Policy — le vôtre plus celui qu’un CDN a ajouté — une ressource doit satisfaire les deux, si bien qu’un en-tête supplémentaire ne fait que resserrer la page.

Référence des directives

DirectiveGouverneRepli sur default-src ?
script-srcéléments script, eval, gestionnaires en ligneoui
style-srcéléments style, liens de feuilles de style, @import, attributs styleoui
connect-srcfetch, XMLHttpRequest, WebSocket, EventSource, sendBeaconoui
img-src / font-src / media-src / manifest-srcimages et srcset ; polices web ; audio, vidéo, track ; manifeste d’applicationoui
object-srcobject et embed — toujours 'none'oui
frame-src / child-src / worker-srcdocuments imbriqués ; workersoui, les workers via child-src
base-uri / form-actionvaleurs qu’une balise base peut fixer ; où les formulaires peuvent soumettrenon
frame-ancestorsqui peut intégrer cette page ; supplante X-Frame-Optionsnon
sandbox / require-trusted-types-fordrapeaux de bac à sable sur ce document ; puits de XSS DOMnon
report-uri / report-tooù les rapports de violation sont envoyés en POSTnon

La colonne de droite est le piège : default-src 'self' laisse encore ouverts le détournement de la balise base, l’exfiltration par formulaire et le détournement de clic. script-src-elem et script-src-attr séparent les éléments des gestionnaires en ligne.

Expressions de source

ExpressionCorrespond àÀ surveiller
'self' / 'none'le schéma, l’hôte et le port exacts du document / rienpas les sous-domaines ; 'none' est nul à côté de toute autre valeur
https:n’importe quel hôte sur ce schémachaque CDN d’internet
https://cdn.example.comcette origine, port par défaut impliciteles préfixes de chemin ne sont pas appliqués à travers les redirections
*.example.com / *n’importe quel sous-domaine, à toute profondeur / n’importe quel hôtepas example.com nu ; * exclut data: et blob:
'nonce-…'les éléments portant l’attribut nonce correspondant128 bits aléatoires ou plus, frais à chaque réponse
'sha256-…'le code en ligne dont les octets exacts produisent cette empreinteun octet d’espace change l’empreinte
'strict-dynamic'les scripts créés par un script déjà approuvéles sources d’hôte et de schéma deviennent ignorées
'unsafe-inline'tout script ou style en ligneignoré dès qu’un nonce ou une empreinte est présent
'unsafe-eval' / 'unsafe-hashes'eval et new Function / empreintes sur onclick, style'wasm-unsafe-eval' en est la version restreinte

Déployer avec Report-Only

Livrez la politique candidate comme Content-Security-Policy-Report-Only : évaluée à l’identique, ne bloquant rien, et servable à côté d’une politique appliquée, ce qui permet d’en resserrer une seconde pendant que celle en vigueur protège les utilisateurs. Collectez les violations avec report-uri /csp-reports (déprécié, universellement pris en charge) ou report-to, qui exige un en-tête Reporting-Endpoints ; la livraison des rapports est exemptée de connect-src. Les blocages multi-origines se réduisent à une origine dans blocked-uri, nommant l’hôte refusé, pas le fichier ; ajoutez 'report-sample' pour un extrait de code et regroupez par effective-directive.

Quel outil pour quel travail

Quand un site est en ligne, la question est quelle politique est réellement livrée. En-têtes HTTP charge l’URL depuis api.sitekits.dev et renvoie le statut, le nombre de redirections et chaque en-tête de réponse, jamais le corps — ce qui révèle un proxy ayant réécrit votre politique. Les adresses internes sont refusées par sa protection SSRF.

Si vous en êtes plutôt à la rédaction, le Générateur CSP offre 14 champs de directives par-dessus une base durcie default-src 'self'; frame-ancestors 'none'; base-uri 'self'; object-src 'none', plus une case upgrade-insecure-requests active par défaut — un formulaire non modifié émet donc déjà cette base avec upgrade-insecure-requests ajouté. La chaîne est reconstruite au fil de la saisie, dans le navigateur. Les directives hors de ces champs (report-to, sandbox, require-trusted-types-for) s’ajoutent à la main.

Établir la liste d’autorisation d’une application existante est un problème d’inventaire : exportez un HAR depuis DevTools, lisez-le dans la Visionneuse HAR, puis passez les URL surprenantes dans le Parseur d’URL pour réduire chacune à la forme schéma-hôte-port qu’une expression de source exige. Assainissez avec le Nettoyeur HAR avant de le joindre à un ticket — les HAR portent des cookies et des en-têtes Authorization. Le Diff de Texte montre ce qui a changé entre les versions report-only et appliquée ; le Formateur JSON rend lisible une charge utile csp-report ; et le Testeur REST API appelle votre cible directement depuis le navigateur, sans serveur sitekits sur le chemin — et sous la politique de sa page, qui assouplit connect-src à 'self' https:, pas sous la vôtre. Un appel qui réussit là mais échoue dans votre application désigne votre propre en-tête. Il ne nommera pas la cause : un blocage connect-src et un rejet CORS se manifestent tous deux par une unique TypeError de fetch, et l’outil imprime un seul message couvrant les deux. Également : hub HTTP, hub sécurité, outils de l’ingénieur sécurité.

Casses courantes

Ajouter un nonce désactive silencieusement 'unsafe-inline'

Les gestionnaires de balises et les widgets de discussion qui injectent leurs propres balises de script cessent de s’exécuter, puisqu’ils ne voient jamais votre valeur par réponse ; 'strict-dynamic' est le remède.

Les empreintes CSP sont du base64 du condensé, pas de l’hexadécimal

'sha256-…' attend du base64 des 32 octets bruts du condensé : de l’hexadécimal issu du Générateur de Hash puis passé dans Base64 donne donc une chaîne erronée. Copiez-la depuis l’erreur affichée dans la console du navigateur.

Un nonce codé en dur, c’est 'unsafe-inline' avec des étapes en plus

Les nonces doivent être régénérés à chaque réponse, ce qui les rend côté serveur ; une constante dans un gabarit — ou du HTML mis en cache en périphérie tandis que l’en-tête se régénère — est devinable. Le Générateur UUID convient pour des essais manuels locaux, jamais pour un déploiement.

Une politique en balise meta ne peut pas exprimer la moitié de la CSP

frame-ancestors, sandbox, report-uri et Report-Only sont ignorés dans meta http-equiv, et la politique ne couvre que le balisage qui la suit.

FAQ
Pourquoi mon script en ligne est-il encore bloqué alors que script-src contient 'unsafe-inline' ?
Parce que la même directive contient aussi un nonce ou une empreinte. Les navigateurs ignorent délibérément 'unsafe-inline' dès qu'une source 'nonce-…' ou 'sha256-…' apparaît dans cette directive : le repli cesse donc silencieusement de s'appliquer. Apposez le nonce courant sur le script en ligne, ajoutez son empreinte, ou ajoutez 'strict-dynamic' pour que les scripts chargés par un script déjà approuvé héritent de la confiance.
default-src couvre-t-il toutes les directives ?
Non, et c'est le trou le plus courant dans une politique par ailleurs stricte. base-uri, form-action, frame-ancestors, sandbox, report-uri et report-to ne se replient pas sur default-src : default-src 'self' seul permet donc encore le détournement de la balise base, la soumission de formulaire vers n'importe quel hôte, et l'encadrement par n'importe quel site. Celles-là doivent être écrites explicitement.
Comment déployer une CSP sans casser la production ?
Envoyez la politique candidate comme Content-Security-Policy-Report-Only, qui est évaluée exactement comme l'en-tête réel mais ne bloque rien et peut être servie à côté d'une politique appliquée. Collectez les rapports pendant une à deux semaines, puis changez le nom de l'en-tête. Attendez-vous à une large part de bruit inexploitable venant des extensions de navigateur, qui injectent des scripts en ligne dans vos pages.
Faut-il utiliser un nonce ou une empreinte pour les scripts en ligne ?
Utilisez un nonce quand le HTML est généré par réponse, puisque la valeur doit être imprévisible et fraîche à chaque fois. Utilisez des empreintes quand le HTML est statique ou mis en cache au CDN, car une empreinte n'exige aucun aléa côté serveur — mais elle couvre les octets exacts du bloc en ligne, si bien qu'une espace modifiée l'invalide. Les deux se combinent avec 'strict-dynamic', qui propage la confiance depuis ce que la directive autorisait déjà : script-src 'sha256-…' 'strict-dynamic' https: 'unsafe-inline' est la politique stricte fondée sur les empreintes documentée pour ce cas statique. La confiance n'atteint que les scripts créés à l'exécution par le bloc dont l'empreinte est connue, pas les balises déjà présentes dans le balisage.
Pourquoi mon rapport de violation ne nomme-t-il qu'un domaine dans blocked-uri au lieu du fichier bloqué ?
Parce que les violations multi-origines sont délibérément réduites à l'origine — communiquer l'URL complète livrerait à la page l'information que la CSP vient justement de l'empêcher de lire. Vous apprenez quel hôte a été refusé, pas quelle ressource, et après une redirection vous pouvez n'obtenir que l'origine initiale. Ajoutez 'report-sample' à la directive pour que les rapports portent un court extrait du code fautif, et regroupez par effective-directive pour voir quelle règle se déclenche réellement. Les violations de même origine ne sont pas tronquées : ces rapports portent donc bien le chemin complet.