La stratégie CSP peut réduire considérablement le risque et l'impact des attaques de script intersites dans les navigateurs modernes.
Le modèle de sécurité du Web est basé sur un règlement de même origine. Par exemple, le code de https://mybank.com
ne doit avoir accès qu'aux données de https://mybank.com
, et https://evil.example.com
ne doit jamais y avoir accès.
En théorie, chaque origine est isolée du reste du Web, ce qui offre aux développeurs un bac à sable sécurisé dans lequel créer. En pratique, cependant, les pirates informatiques ont trouvé plusieurs façons de subvertir le système.
Les attaques de script intersites (XSS), par exemple, contournent la règle de même origine en incitant un site à diffuser du code malveillant avec le contenu prévu. Il s'agit d'un problème majeur, car les navigateurs font confiance à tout le code qui s'affiche sur une page comme faisant légitimement partie de l'origine de sécurité de cette page. L'aide-mémoire XSS est un exemple ancien mais représentatif des méthodes qu'un pirate informatique pourrait utiliser pour violer cette confiance en injectant du code malveillant. Si un pirate informatique parvient à injecter n'importe quel code, il compromet la session utilisateur et accède à des informations privées.
Cette page présente la stratégie Content Security Policy (CSP) pour réduire le risque et l'impact des attaques XSS dans les navigateurs modernes.
Composants du CSP
Pour implémenter un CSP efficace, procédez comme suit :
- Utilisez des listes d'autorisation pour indiquer au client ce qui est autorisé et ce qui ne l'est pas.
- Découvrez les directives disponibles.
- Découvrez les mots clés qu'ils utilisent.
- Limitez l'utilisation du code intégré et de
eval()
. - Signalez les cas de non-respect des règles à votre serveur avant de les appliquer.
Listes d'autorisation sources
Les attaques XSS exploitent l'incapacité du navigateur à faire la distinction entre le script qui fait partie de votre application et le script qui a été injecté de manière malveillante par un tiers. Par exemple, le bouton Google +1 en bas de cette page charge et exécute le code de https://apis.google.com/js/plusone.js
dans le contexte de l'origine de cette page.
Nous faisons confiance à ce code, mais nous ne pouvons pas nous attendre à ce que le navigateur détermine lui-même que le code de apis.google.com
peut être exécuté en toute sécurité, alors que celui de apis.evil.example.com
ne l'est probablement pas. Le navigateur télécharge et exécute volontiers tout code qu'une page demande, quelle que soit la source.
L'en-tête HTTP Content-Security-Policy
de la CSP vous permet de créer une liste d'autorisation de sources de contenu fiables, et indique au navigateur de n'exécuter ou d'afficher que des ressources provenant de ces sources. Même si un pirate informatique parvient à trouver une faille pour injecter un script, celui-ci ne correspondra pas à la liste d'autorisation et ne sera donc pas exécuté.
Nous faisons confiance à apis.google.com
pour fournir du code valide, et nous nous faisons confiance pour faire de même. Voici un exemple de stratégie qui n'autorise l'exécution des scripts que lorsqu'ils proviennent de l'une de ces deux sources :
Content-Security-Policy: script-src 'self' https://apis.google.com
script-src
est une directive qui contrôle un ensemble de droits liés aux scripts pour une page. Cet en-tête 'self'
est une source de script valide, et https://apis.google.com
en est une autre. Le navigateur peut désormais télécharger et exécuter du code JavaScript à partir de apis.google.com
via HTTPS, ainsi que depuis l'origine de la page actuelle, mais pas depuis une autre origine. Si un pirate informatique injecte du code dans votre site, le navigateur génère une erreur et n'exécute pas le script injecté.
La stratégie s'applique à un large éventail de ressources
Le CSP fournit un ensemble de directives de stratégie qui permettent de contrôler précisément les ressources qu'une page est autorisée à charger, y compris script-src
de l'exemple précédent.
La liste suivante décrit le reste des directives de ressources au niveau 2. Une spécification de niveau 3 a été rédigée, mais elle n'est largement pas implémentée dans les principaux navigateurs.
base-uri
- Limite les URL pouvant apparaître dans l'élément
<base>
d'une page. child-src
- Liste les URL des nœuds de calcul et les contenus des cadres intégrés. Par exemple,
child-src https://youtube.com
permet d'intégrer des vidéos YouTube, mais pas d'autres sources. connect-src
- Limite les origines auxquelles vous pouvez vous connecter à l'aide de XHR, de WebSockets et d'EventSource.
font-src
- Spécifie les origines pouvant diffuser des polices Web. Par exemple, vous pouvez autoriser les polices Web de Google à l'aide de
font-src https://themes.googleusercontent.com
. form-action
- Indique les points de terminaison valides pour l'envoi à partir des balises
<form>
. frame-ancestors
- Spécifie les sources pouvant intégrer la page actuelle. Cette directive s'applique aux balises
<frame>
,<iframe>
,<embed>
et<applet>
. Il ne peut pas être utilisé dans les balises<meta>
ni pour les ressources HTML. frame-src
- Cette directive a été abandonnée au niveau 2, mais a été restaurée au niveau 3. S'il n'est pas présent, le navigateur utilise
child-src
. img-src
- Définit les origines à partir desquelles les images peuvent être chargées.
media-src
- Limite les origines autorisées à diffuser des contenus vidéo et audio.
object-src
- Permet de contrôler Flash et d'autres plug-ins.
plugin-types
- Limite les types de plug-ins qu'une page peut appeler.
report-uri
- Spécifie une URL à laquelle le navigateur envoie des rapports lorsqu'une stratégie de sécurité du contenu est enfreinte. Cette directive ne peut pas être utilisée dans les balises
<meta>
. style-src
- Limite les origines à partir desquelles une page peut utiliser des feuilles de style.
upgrade-insecure-requests
- Indique aux agents utilisateur de réécrire les schémas d'URL en remplaçant HTTP par HTTPS. Cette directive s'applique aux sites Web comportant un grand nombre d'anciennes URL à réécrire.
worker-src
- Directive de niveau 3 du CSP qui limite les URL pouvant être chargées en tant que worker, worker partagé ou service worker. Depuis juillet 2017, cette directive a des implémentations limitées.
Par défaut, le navigateur charge la ressource associée à partir de n'importe quelle origine, sans restriction, sauf si vous définissez une stratégie avec une directive spécifique. Pour remplacer la valeur par défaut, spécifiez une directive default-src
. Cette directive définit les valeurs par défaut de toute directive non spécifiée se terminant par -src
. Par exemple, si vous définissez default-src
sur https://example.com
et que vous ne spécifiez pas de directive font-src
, vous ne pouvez charger des polices qu'à partir de https://example.com
.
Les directives suivantes n'utilisent pas default-src
comme solution de remplacement. N'oubliez pas que l'échec de leur définition revient à autoriser quoi que ce soit:
base-uri
form-action
frame-ancestors
plugin-types
report-uri
sandbox
Syntaxe de base du CSP
Pour utiliser des directives CSP, listez-les dans l'en-tête HTTP, en les séparant par des deux-points. Veillez à lister toutes les ressources requises d'un type spécifique dans une seule directive, comme suit :
script-src https://host1.com https://host2.com
Vous trouverez ci-dessous un exemple de plusieurs directives, dans ce cas pour une application Web qui charge toutes ses ressources à partir d'un réseau de diffusion de contenu sur https://cdn.example.net
et qui n'utilise pas de contenu ni de plug-in encadrés :
Content-Security-Policy: default-src https://cdn.example.net; child-src 'none'; object-src 'none'
Détails de mise en œuvre
Les navigateurs modernes acceptent l'en-tête Content-Security-Policy
sans préfixe.
Il s'agit de l'en-tête recommandé. Les en-têtes X-WebKit-CSP
et X-Content-Security-Policy
que vous pouvez voir dans les tutoriels en ligne sont obsolètes.
La CSP est définie page par page. Vous devez envoyer l'en-tête HTTP avec chaque réponse que vous souhaitez protéger. Vous pouvez ainsi adapter la règle à des pages spécifiques en fonction de leurs besoins spécifiques. Par exemple, si un ensemble de pages de votre site comporte un bouton +1, tandis que d'autres n'en ont pas, vous pouvez autoriser le code du bouton à se charger uniquement lorsque cela est nécessaire.
La liste source de chaque directive est flexible. Vous pouvez spécifier des sources par schéma (data:
, https:
), ou en fonction de leur spécificité, du nom d'hôte uniquement (example.com
, qui correspond à n'importe quelle origine sur cet hôte : n'importe quel schéma, n'importe quel port) à un URI complet (https://example.com:443
, qui ne correspond qu'à HTTPS, qu'à example.com
et qu'au port 443). Les caractères génériques sont acceptés, mais uniquement en tant que schéma, port ou à la position la plus à gauche du nom d'hôte : *://*.example.com:*
correspond à tous les sous-domaines de example.com
(mais pas example.com
lui-même), à l'aide de n'importe quel schéma, sur n'importe quel port.
La liste de sources accepte également quatre mots clés :
'none'
ne correspond à rien.'self'
correspond à l'origine actuelle, mais pas à ses sous-domaines.'unsafe-inline'
permet d'intégrer JavaScript et CSS. Pour en savoir plus, consultez Éviter le code intégré.'unsafe-eval'
permet d'utiliser des mécanismes de conversion texte-JavaScript tels queeval
. Pour en savoir plus, consultez la section Évitereval()
.
Ces mots clés doivent être placés entre guillemets simples. Par exemple, script-src 'self'
(avec guillemets) autorise l'exécution de JavaScript à partir de l'hôte actuel. script-src self
(sans guillemets) autorise JavaScript à partir d'un serveur nommé "self
" (et pas à partir de l'hôte actuel), ce qui n'est probablement pas ce que vous vouliez dire.
Créez un bac à sable pour vos pages
Voici une autre directive qui mérite d'être évoquée: sandbox
. Il est un peu différent des autres que nous avons examinés, car il impose des restrictions sur les actions que la page peut effectuer plutôt que sur les ressources qu'elle peut charger. Si l'instruction sandbox
est présente, la page est traitée comme si elle avait été chargée dans un élément <iframe>
avec un attribut sandbox
. Cela peut avoir un large éventail d'effets sur la page: forcer la page à avoir une origine unique et empêcher l'envoi de formulaire, entre autres. Cette opération dépasse un peu le cadre de cette page, mais vous trouverez des informations complètes sur les attributs de bac à sable valides dans la section "Bac à sable" de la spécification HTML5.
La balise Meta
Le mécanisme de diffusion privilégié des CSP est un en-tête HTTP. Toutefois, il peut être utile de définir une règle sur une page directement dans le balisage. Pour ce faire, utilisez une balise <meta>
avec un attribut http-equiv
:
<meta http-equiv="Content-Security-Policy" content="default-src https://cdn.example.net; child-src 'none'; object-src 'none'">
Cette option ne peut pas être utilisée pour frame-ancestors
, report-uri
ou sandbox
.
Éviter le code intégré
Aussi puissantes que soient les listes d'autorisation basées sur l'origine utilisées dans les directives CSP, elles ne peuvent pas résoudre la plus grande menace posée par les attaques XSS : l'injection de script en ligne.
Si un pirate informatique peut injecter une balise de script contenant directement une charge utile malveillante (comme <script>sendMyDataToEvilDotCom()</script>
), le navigateur n'a aucun moyen de la distinguer d'une balise de script intégrée légitime. CSP résout ce problème en interdisant complètement le script intégré.
Cette interdiction concerne non seulement les scripts intégrés directement dans les balises script
, mais aussi les gestionnaires d'événements intégrés et les URL javascript:
. Vous devez déplacer le contenu des balises script
dans un fichier externe, et remplacer les URL javascript:
et <a ...
onclick="[JAVASCRIPT]">
par les appels addEventListener()
appropriés. Par exemple, vous pouvez réécrire le code suivant à partir de :
<script>
function doAmazingThings() {
alert('YOU ARE AMAZING!');
}
</script>
<button onclick='doAmazingThings();'>Am I amazing?</button>
à quelque chose comme :
<!-- amazing.html -->
<script src='amazing.js'></script>
<button id='amazing'>Am I amazing?</button>
// amazing.js
function doAmazingThings() {
alert('YOU ARE AMAZING!');
}
document.addEventListener('DOMContentLoaded', function () {
document.getElementById('amazing')
.addEventListener('click', doAmazingThings);
});
Le code réécrit est non seulement compatible avec le CSP, mais aussi conforme aux bonnes pratiques de conception Web. Le code JavaScript intégré mélange la structure et le comportement de manière à rendre le code déroutant. Il est également plus compliqué à mettre en cache et à compiler. En déplaçant votre code vers des ressources externes, vous améliorez les performances de vos pages.
Il est également fortement recommandé de déplacer les balises et attributs style
intégrés vers des feuilles de style externes pour protéger votre site contre les attaques d'exfiltration de données basées sur CSS.
Autoriser temporairement les scripts et styles intégrés
Vous pouvez activer les scripts et les styles intégrés en ajoutant 'unsafe-inline'
en tant que source autorisée dans une instruction script-src
ou style-src
. Le niveau 2 du CSP vous permet également d'ajouter des scripts intégrés spécifiques à votre liste d'autorisation à l'aide d'un nonce cryptographique (nombre utilisé une seule fois) ou d'un hachage comme suit.
Pour utiliser un nonce, attribuez un attribut nonce à votre balise de script. Sa valeur doit correspondre à l'une des sources approuvées de la liste. Exemple :
<script nonce="EDNnf03nceIOfn39fn3e9h3sdfa">
// Some inline code I can't remove yet, but need to as soon as possible.
</script>
Ajoutez le nonce à votre directive script-src
après le mot clé nonce-
:
Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'
Les nonces doivent être générés à nouveau pour chaque requête de page et ne doivent pas être devinés.
Les hachages fonctionnent de manière similaire. Au lieu d'ajouter du code à la balise de script, créez un hachage SHA du script lui-même et ajoutez-le à la directive script-src
.
Par exemple, si votre page contenait ceci :
<script>alert('Hello, world.');</script>
Vos règles doivent contenir les éléments suivants:
Content-Security-Policy: script-src 'sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng='
Le préfixe sha*-
spécifie l'algorithme qui génère le hachage. L'exemple précédent utilise sha256-
, mais le CSP est également compatible avec sha384-
et sha512-
. Lors de la génération du hachage, omettez les balises <script>
. La capitalisation et les espaces sont importants, y compris les espaces de début et de fin.
Des solutions permettant de générer des hachages SHA sont disponibles dans toutes les langues. À partir de la version 40 de Chrome, vous pouvez ouvrir les outils de développement, puis actualiser la page. L'onglet "Console" affiche les messages d'erreur avec le hachage SHA-256 correct pour chacun de vos scripts intégrés.
Éviter la requête eval()
Même si un pirate informatique ne peut pas injecter de script directement, il peut être en mesure de duper votre application pour qu'elle convertisse le texte saisi en code JavaScript exécutable et l'exécute en son nom. eval()
, new Function()
, setTimeout([string], …)
et setInterval([string], ...)
sont tous des vecteurs que les pirates informatiques peuvent utiliser pour exécuter du code malveillant via du texte injecté. La réponse par défaut de CSP à ce risque consiste à bloquer complètement tous ces vecteurs.
Cela aura les conséquences suivantes sur la façon dont vous créez des applications:
- Vous devez analyser le JSON à l'aide de
JSON.parse
intégré, au lieu de vous appuyer sureval
. Les opérations JSON sécurisées sont disponibles dans tous les navigateurs depuis IE8. Vous devez réécrire tous les appels
setTimeout
ousetInterval
que vous effectuez à l'aide de fonctions intégrées au lieu de chaînes. Par exemple, si votre page contient les éléments suivants :setTimeout("document.querySelector('a').style.display = 'none';", 10);
Réécrivez-la comme suit :
setTimeout(function () { document.querySelector('a').style.display = 'none'; }, 10); ```
Évitez d'utiliser des modèles intégrés au moment de l'exécution. De nombreuses bibliothèques de modèles utilisent souvent
new Function()
pour accélérer la génération de modèles au moment de l'exécution, ce qui permet d'évaluer le texte malveillant. Certains frameworks sont compatibles avec le CSP dès le départ, et reviennent à un analyseur robuste en l'absence deeval
. La directive ng-csp d'AngularJS en est un bon exemple. Toutefois, nous vous recommandons d'utiliser un langage de création de modèles qui propose une précompilation, comme Handlebars. La précompilation de vos modèles peut rendre l'expérience utilisateur encore plus rapide que l'implémentation d'exécution la plus rapide, tout en rendant votre site plus sécurisé.
Si eval()
ou d'autres fonctions de conversion de texte en JavaScript sont essentielles à votre application, vous pouvez les activer en ajoutant 'unsafe-eval'
en tant que source autorisée dans une directive script-src
. Nous vous déconseillons vivement de le faire en raison du risque d'injection de code qu'il présente.
Signaler un cas de non-respect des règles
Pour avertir le serveur des bugs susceptibles d'autoriser une injection malveillante, vous pouvez demander au navigateur d'POST
envoyer des rapports de non-respect au format JSON à un emplacement spécifié dans une directive report-uri
:
Content-Security-Policy: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;
Ces rapports se présentent comme suit:
{
"csp-report": {
"document-uri": "http://example.org/page.html",
"referrer": "http://evil.example.com/",
"blocked-uri": "http://evil.example.com/evil.js",
"violated-directive": "script-src 'self' https://apis.google.com",
"original-policy": "script-src 'self' https://apis.google.com; report-uri http://example.org/my_amazing_csp_report_parser"
}
}
Le rapport contient des informations utiles pour identifier la cause d'un non-respect des règles, y compris la page concernée (document-uri
), le referrer
de cette page, la ressource qui ne respecte pas le règlement de la page (blocked-uri
), la directive spécifique qu'elle ne respecte pas (violated-directive
) et le règlement complet de la page (original-policy
).
Rapports uniquement
Si vous débutez avec le CSP, nous vous recommandons d'utiliser le mode "Rapports uniquement" pour évaluer l'état de votre application avant de modifier votre règle. Pour ce faire, au lieu d'envoyer un en-tête Content-Security-Policy
, envoyez un en-tête Content-Security-Policy-Report-Only
:
Content-Security-Policy-Report-Only: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;
La règle spécifiée en mode "Rapport uniquement" ne bloque pas les ressources limitées, mais envoie des rapports de non-respect à l'emplacement que vous spécifiez. Vous pouvez même envoyer les deux en-têtes pour appliquer une règle tout en en surveillant une autre. C'est un excellent moyen de tester les modifications apportées à votre CSP tout en appliquant votre règle actuelle: activez la création de rapports pour une nouvelle règle, surveillez les rapports de non-respect et corrigez les bugs, puis commencez à l'appliquer dès que la nouvelle règle vous convient.
Utilisation réelle
La première étape de la création d'une règle pour votre application consiste à évaluer les ressources qu'elle charge. Une fois que vous avez compris la structure de votre application, créez une règle en fonction de ses exigences. Les sections suivantes présentent quelques cas d'utilisation courants et le processus de prise en charge de ces cas conformément aux consignes du CSP.
Widgets de réseaux sociaux
- Le bouton "J'aime" de Facebook propose plusieurs options d'implémentation. Nous vous recommandons d'utiliser la version de
<iframe>
pour qu'elle reste en bac à sable du reste de votre site. Il a besoin d'une directivechild-src https://facebook.com
pour fonctionner correctement. - Le bouton Tweet de X repose sur l'accès à un script.
Déplacez le script qu'il fournit dans un fichier JavaScript externe et utilisez la directive
script-src https://platform.twitter.com; child-src https://platform.twitter.com
. - Les autres plates-formes ont des exigences similaires et peuvent être traitées de la même manière.
Pour tester ces ressources, nous vous recommandons de définir un
default-src
sur'none'
et de surveiller votre console pour déterminer les ressources que vous devrez activer.
Pour utiliser plusieurs widgets, combinez les directives comme suit :
script-src https://apis.google.com https://platform.twitter.com; child-src https://plusone.google.com https://facebook.com https://platform.twitter.com
Verrouiller
Pour certains sites Web, vous devez vous assurer que seules les ressources locales peuvent être chargées. L'exemple suivant développe un CSP pour un site bancaire, en commençant par une stratégie par défaut qui bloque tout (default-src 'none'
).
Le site charge toutes les images, tous les styles et tous les scripts à partir d'un CDN à l'adresse https://cdn.mybank.net
, et se connecte à https://api.mybank.com/
à l'aide de XHR pour récupérer les données. Il utilise des cadres, mais uniquement pour les pages locales du site (pas d'origines tierces). Le site ne comporte pas de Flash, de polices ni d'éléments supplémentaires. L'en-tête CSP le plus restrictif qu'il peut envoyer est le suivant :
Content-Security-Policy: default-src 'none'; script-src https://cdn.mybank.net; style-src https://cdn.mybank.net; img-src https://cdn.mybank.net; connect-src https://api.mybank.com; child-src 'self'
SSL uniquement
Voici un exemple de CSP pour un administrateur de forum qui souhaite s'assurer que toutes les ressources de son forum ne sont chargées qu'à l'aide de canaux sécurisés, mais qui n'a pas d'expérience en codage et ne dispose pas des ressources nécessaires pour réécrire le logiciel de forum tiers rempli de scripts et de styles intégrés :
Content-Security-Policy: default-src https:; script-src https: 'unsafe-inline'; style-src https: 'unsafe-inline'
Bien que https:
soit spécifié dans default-src
, les directives de script et de style n'héritent pas automatiquement de cette source. Chaque directive écrase la valeur par défaut de ce type de ressource spécifique.
Développement standard CSP
Le niveau 2 de la règle de sécurité du contenu est une norme recommandée par le W3C. Le groupe de travail sur la sécurité des applications Web du W3C développe la prochaine itération de la spécification, la Content Security Policy Level 3.
Pour suivre la discussion sur ces fonctionnalités à venir, consultez les archives de la liste de diffusion public-webappsec@.