Content Security Policy (CSP) est une mesure de sécurité essentielle pour protéger les applications web contre certains types d’attaques. En définissant des règles strictes sur les ressources qu’un navigateur peut charger, une CSP limite en effet les vecteurs d’attaque potentiels.
Cependant, une Content Security Policy mal configurée peut être contournée, rendant l’application vulnérable.
Cet article explore les principes des CSP, leurs directives clés et les erreurs courantes qui permettent de les contourner. Nous verrons également comment tester leur efficacité et appliquer les bonnes pratiques pour renforcer leur sécurité.
Guide complet sur les CSP (Content Security Policy)
- Principes et fonctionnement d'une Content Security Policy
- Principales directives d’une Content Security Policy ?
- Les valeurs possibles pour les directives de récupération
- Exemple d’une Content Security Policy
- Configurations et techniques permettant de contourner une Content Security Policy
- Comment tester l'efficacité d'une Content Security Policy ?
- Bonnes pratiques pour renforcer la sécurité des CSP
Principes et fonctionnement d’une Content Security Policy
Une Content Security Policy (CSP) est une protection supplémentaire qui aide à détecter et limiter certaines attaques côté client, comme le Cross Site Scripting (XSS), le clickjacking ou l’injection de contenu.
Elle se configure au niveau du serveur. Les administrateurs définissent des règles que les navigateurs doivent respecter lors du chargement d’une page. Par exemple, ils peuvent préciser quelles sources sont autorisées pour les scripts, images et autres ressources.
Il existe deux manières de configurer une CSP :
- Ajouter un en-tête
Content-Security-Policy
dans les réponses du serveur. - Utiliser l’élément HTML
<meta>
dans les pages de l’application.
Quelles sont les principales directives d’une Content Security Policy ?
Une Content Security Policy est composée de règles appelées directives. Chaque directive définit des restrictions spécifiques sur le chargement et l’exécution de certaines ressources.
Il existe plusieurs types de directives. Celles dites de récupération indiquent au navigateur comment charger les données. D’autres directives contrôlent la navigation, les documents, ou encore le reporting.
Directives de récupération
Ces directives définissent les sources autorisées pour différentes ressources HTML :
- default-src : Source par défaut si une autre directive n’est pas définie.
- script-src : Sources valides pour les scripts JavaScript et WebAssembly.
- script-src-elem : Sources valides pour les balises
<script>
. Si absente, c’estscript-src
qui s’applique. - frame-src : Sources valides pour les
<frame>
et<iframe>
. - img-src : Sources valides pour les images.
- object-src : Sources valides pour
<object>
,<embed>
et<applet>
. - style-src : Sources valides pour les feuilles de style.
- font-src : Sources valides pour les polices de caractères.
Autres directives importantes
Certaines directives ne concernent pas la récupération des ressources mais ajoutent des contrôles de sécurité supplémentaires :
- sandbox : Active une sandbox pour isoler certains contenus (comme pour les
<iframe>
). - require-trusted-types-for : Imposent l’utilisation de “trusted types” pour limiter les attaques DOM-based XSS.
- trusted-types : Définit une liste blanche de « Trusted Types » pour éviter l’exécution de données usurpées.
- upgrade-insecure-requests : Convertit automatiquement les requêtes HTTP en HTTPS. Utile pour moderniser un site avec de nombreuses anciennes URLs.
- frame-ancestors : Restreint les sources autorisées pour les éléments
<frame>
,<iframe>
,<object>
,<embed>
et<applet>
. - form-action : Contrôle les URLs autorisées pour l’envoi de formulaires.
- base-uri : Restreint les sources valides pour la balise
<base>
.
Ces directives permettent de renforcer la sécurité des applications web en limitant les vecteurs d’attaque courants.
Les valeurs possibles pour les directives de récupération
Chaque directive de récupération peut être associée à différentes valeurs pour définir les sources autorisées. Voici quelques-unes des valeurs possibles et leur effet :
none
: Bloque totalement la ressource.self
: Autorise uniquement le chargement depuis la même origine que le document.[host-source]
: Spécifie une URL ou une adresse IP valide, par exemplehttps://vaadata.com
.[scheme-source]
: Autorise un schéma spécifique commehttps:
,http:
,ws:
,data:
, etc.*
(wildcard) : Autorise toutes les valeurs légales pour les sous-domaines, hôtes ou ports, par exemplehttps://*.vaadata.com
.nonce-[nonce_value]
: Utilise une valeur aléatoire (nonce_value
) générée par le serveur pour chaque réponse HTTP. Chaque balise HTML ciblée doit inclure ce nonce afin que le navigateur puisse vérifier sa validité avant d’exécuter le script ou le style.unsafe-eval
: Autorise l’exécution de JavaScript via des fonctions commeeval()
,Function()
, ousetTimeout("code")
. Par défaut, cette option est désactivée sidefault-src
ouscript-src
est défini.unsafe-inline
: Permet l’exécution de scripts intégrés (<script>
), d’attributs d’événements (onclick
, etc.), et d’URLs enjavascript:
. Par défaut, cette option est désactivée sidefault-src
,script-src
oustyle-src
est défini.
Ces valeurs influencent directement la sécurité et la permissivité d’une CSP. Une mauvaise configuration peut exposer une application à des attaques XSS.
Exemple d’une Content Security Policy
Maintenant que nous avons vu les directives et leurs valeurs, prenons un exemple concret pour mieux comprendre le fonctionnement d’une CSP.
Lorsqu’un serveur web envoie une page au navigateur, il peut inclure une CSP dans la réponse HTTP. Voici un exemple :
Requête envoyée au serveur
GET / HTTP/2
Host: whatever.vaadata.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:134.0) Gecko/20100101 Firefox/134.0
Réponse du serveur
HTTP/2 200 OK
Content-Type: text/html
Strict-Transport-Security: max-age=63072000
Content-Security-Policy: default-src 'self'; script-src 'self' https://*.vaadata.com; object-src 'none'; img-src 'self' data: *.vaadata.com;
X-Frame-Options: DENY
Server: Nginx
Content-Length: 10236
Vary: Accept-Encoding
X-Cache: miss
[…TRUNCATED DATA…]
Dans la réponse précédente, la Content Security Policy appliquée à la page du site https://whatever.vaadata.com
définit plusieurs règles de sécurité. Voici leur interprétation :
default-src 'self'
: Toute directive de récupération non explicitement définie utilisera cette valeur par défaut. Ici, seules les ressources provenant dewhatever.vaadata.com
sont autorisées.script-src 'self' https://*.vaadata.com
: Seules les sourceswhatever.vaadata.com
et les sous-domaines devaadata.com
en HTTPS peuvent charger du JavaScript.- Un script chargé depuis
https://bonjour.vaadata.com
sera autorisé. - Un script chargé depuis
https://hello.vaadata.at
sera bloqué, car il n’appartient pas au domainevaadata.com
.
- Un script chargé depuis
object-src 'none'
: L’utilisation des balises<object>
et<embed>
est totalement interdite.img-src 'self' data: *.vaadata.com
:- Les images peuvent être chargées depuis
whatever.vaadata.com
(self
). - Le schéma
data:
est autorisé, permettant l’intégration d’images en Base64. - Toutes les images provenant des sous-domaines de
vaadata.com
sont acceptées.
- Les images peuvent être chargées depuis
Cette configuration restreint les sources externes pour réduire les risques d’injection et de chargement de contenus malveillants.
Configurations et techniques permettant de contourner une Content Security Policy
Une CSP mal configurée peut contenir des failles exploitables. Ces erreurs permettent de contourner la protection et d’exécuter du JavaScript malveillant via des attaques XSS ou d’injection de contenu.
Voici quelques scénarios courants d’exploitation.
Autorisation du JavaScript inline (unsafe-inline)
Content-Security-Policy: default-src 'none'; script-src 'unsafe-inline';
Le problème ici est que le JavaScript inline est autorisé. De fait, si une faille XSS est présente, le payload suivant fonctionnerait :
<script>alert(1);</script>
Autorisation de unsafe-eval
Content-Security-Policy: default-src 'none'; script-src 'unsafe-eval' data:;
Dans ce cas, la directive script-src
autorise l’exécution de code potentiellement dangereux en permettant l’utilisation de fonctions qui évaluent du texte comme du JavaScript.
De plus, la présence de data:
dans la CSP permet d’inclure des scripts encodés en Base64, qui seront interprétés directement par le navigateur.
Ainsi, le payload suivant permet de contourner cette restriction et d’exécuter du code JavaScript :
<script src="data:;base64, YWxlcnQoMSk="></script>
Si la directive script-src
n’autorisait pas data:
, il serait impossible d’exécuter un script directement de cette manière. Toutefois, l’application resterait vulnérable à une attaque DOM XSS si un paramètre contrôlé par l’utilisateur était utilisé dans une fonction comme eval()
.
Utilisation d’une Wildcard dans script-src
Content-Security-Policy: default-src 'none'; script-src https://vaadata.com *;
Dans cet exemple, la directive script-src
contient une wildcard (*
), ce qui signifie que les scripts peuvent être chargés depuis n’importe quelle source. Ce paramètre annule toute restriction potentiellement mise en place par d’autres règles définissant des URL spécifiques.
Un attaquant pourrait exploiter cette faille en hébergeant un script malveillant sur un serveur sous son contrôle, comme evil.vaadata.at
, et l’inclure sur la page ciblée à l’aide des payloads suivants :
<script src="https://evil.vaadata.at"></script>
<script src="data:;base64, YWxlcnQoMSk="></script>
Absence des directives object-src et default-src
Content-Security-Policy: script-src ‘self’; img-src ‘self’; img-src 'self’;
Dans cet exemple, bien que les directives script-src
et img-src
soient définies pour limiter les sources autorisées à la même origine ('self'
), il manque les directives object-src
et default-src
. Cela permet à un attaquant d’exploiter cette configuration en utilisant un élément <object>
pour injecter du code malveillant.
Le payload suivant permet de contourner la CSP et de réaliser une attaque XSS :
<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="></object>
Cette méthode fonctionne car l’élément <object>
n’est pas restreint par la politique de sécurité, permettant ainsi l’exécution du code JavaScript contenu dans la donnée encodée en Base64.
Exploitation d’un endpoint JSONP autorisé
Content-Security-Policy: default-src 'none'; script-src https://hello.vaadata.com/test.js https://accounts.google.com/o/oauth2/revoke;
Cette technique de contournement d’une Content Security Policy repose sur l’exploitation des endpoints JSONP présents dans les domaines autorisés. Ces endpoints permettent de contourner la same-origin policy et de charger des données à partir d’autres origines, tout en injectant du JavaScript dans la réponse sous forme de JSON.
Dans l’exemple donné, l’URL https://accounts.google.com/o/oauth2/revoke
correspond à un endpoint JSONP connu. En envoyant une requête avec un paramètre callback, le serveur répond avec du JavaScript, ce qui permet d’injecter un payload malveillant. Voici un exemple de requête qui déclenche cette réponse JavaScript :
URL de la requête :https://accounts.google.com/o/oauth2/revoke?callback=alert(1)
Requête :
GET /o/oauth2/revoke?callback=alert(1) HTTP/2
Host: accounts.google.com
Réponse :
La réponse retournée contient du JavaScript exécutable, ce qui permet d’exécuter la fonction alert(1)
via un script chargé depuis cet endpoint. Le payload suivant contourne ainsi la CSP :
<script src="https://accounts.google.com/o/oauth2/revoke?callback=alert(1)"></script>
Il est donc essentiel de vérifier si des endpoints JSONP sont présents dans les domaines autorisés par la CSP, car ces points d’entrée peuvent permettre l’exécution de JavaScript malveillant.
Contournement de CSP via l’upload de fichiers
Content-Security-Policy: default-src 'self';
Dans ce cas, la CSP semble sécurisée, car elle limite les ressources autorisées à celles provenant de la même origine que le document.
Cependant, le contournement de la CSP pourrait survenir via une autre fonctionnalité présente sur la plateforme, telle qu’une fonctionnalité d’upload de fichiers. Si cette fonctionnalité permet l’upload de fichiers texte ou de code JavaScript, un attaquant pourrait y télécharger un fichier contenant un payload malveillant.
Si des restrictions sont en place concernant les types de fichiers autorisés à être uploadés, des contournements peuvent néanmoins être possibles.
Une fois le fichier malveillant uploadé, l’attaquant pourrait référencer ce fichier via un chemin relatif et l’inclure dans la page via une balise <script>
comme suit :
<script src="/upload/evil-script.js"></script>
Comment tester l’efficacité d’une Content Security Policy ?
Pour évaluer si votre configuration CSP est optimale, Google met à disposition un outil permettant d’identifier les problèmes de configuration susceptibles d’affecter la sécurité : CSP Evaluator.
Prenons l’exemple de la politique suivante :
Content-Security-Policy: default-src 'self'; script-src 'unsafe-eval' *; script-src-elem https://accounts.google.com/*; img-src ‘unsafe-eval’ data:;
En la soumettant à CSP Evaluator, on obtient une analyse détaillée des directives présentes.
L’outil met en évidence les éventuels risques associés et signale, en bleu, les directives absentes qui sont essentielles pour renforcer la sécurité.
En suivant les recommandations fournies, il devient ainsi possible d’améliorer sa CSP afin de la rendre plus robuste et sécurisée.
Bonnes pratiques pour renforcer la sécurité des CSP
Comme nous l’avons vu, la configuration des CSP peut parfois être contournée en fonction des spécificités de l’application. Il est donc essentiel de comprendre que la sécurité d’une application ne doit pas uniquement reposer sur ces politiques.
Les CSP constituent une couche de protection supplémentaire (defense in depth) qui peut limiter ou empêcher l’exploitation de vulnérabilités telles que le Cross-Site Scripting (XSS) ou l’injection de contenu.
La première ligne de défense contre ces attaques reste la validation des entrées et l’encodage des sorties.
De manière générale, les points suivants doivent être respectés au niveau de votre Content Security Policy :
- Définir une directive default-src restrictive.
- Appliquer une directive object-src stricte (idéalement
none
). - Éviter les valeurs
unsafe-eval
etunsafe-inline
. D’une manière générale, bannir les directives contenant « unsafe », sauf si leurs implications sont parfaitement maîtrisées. - Ne pas autoriser le chargement de scripts depuis des serveurs externes.
- Réduire au maximum l’usage des wildcards (
*
). - Mettre en place des nonces pour renforcer la sécurité.
- Évaluer la robustesse de votre CSP en utilisant des outils spécialisés (voir la section précédente).
Auteur : Yoan MONTOYA – Pentester @Vaadata