Règle de sécurité du contenu

Content Security Policy peut réduire de manière significative le risque et l'impact des attaques par script intersites dans les navigateurs modernes.

Joe Medley
Joe Medley

Navigateurs pris en charge

  • 25
  • 14
  • 23
  • 7

Source

Le modèle de sécurité du Web est basé sur des règles d'origine identique. Par exemple, le code de https://mybank.com ne doit avoir accès qu'aux données de https://mybank.com, et l'accès https://evil.example.com ne doit jamais être autorisé. En théorie, chaque origine est isolée du reste du Web, ce qui offre aux développeurs un bac à sable sécurisé pour l'intégration. En pratique, cependant, les pirates ont trouvé plusieurs moyens de submerger le système.

Les attaques de type script intersites (XSS), par exemple, contournent les règles de même origine en incitant un site à fournir du code malveillant avec le contenu souhaité. Il s'agit d'un problème majeur, car les navigateurs font confiance à l'ensemble du code qui apparaît sur une page comme faisant partie légitime de l'origine de sécurité de cette page. L'aide-mémoire XSS est un aperçu ancien, mais représentatif des méthodes qu'un pirate informatique peut utiliser pour violer cette confiance en injectant du code malveillant. Si un pirate informatique parvient à injecter n'importe quel code, il a compromis la session utilisateur et a obtenu l'accès à des informations privées.

Cette page présente Content Security Policy (CSP) comme stratégie de réduction du risque et de l'impact des attaques XSS dans les navigateurs modernes.

Composants de CSP

Pour implémenter une CSP efficace, procédez comme suit:

  • Utilisez des listes d'autorisation pour indiquer au client ce qui est autorisé ou non.
  • Découvrez les directives disponibles.
  • Identifier les mots clés utilisés
  • Limitez l'utilisation du code intégré et de eval().
  • Signalez à votre serveur les cas de non-respect des règles avant de les appliquer.

Listes d'autorisation de sources

Les attaques XSS exploitent l'incapacité du navigateur à faire la distinction entre un script faisant partie de votre application et un script injecté de manière malveillante par un tiers. Par exemple, le bouton +1 de Google 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écouvre lui-même que le code de apis.google.com peut être exécuté sans risque, contrairement au code de apis.evil.example.com. Le navigateur télécharge et exécute avec plaisir tout code qu'une page demande, quelle que soit la source.

L'en-tête HTTP Content-Security-Policy de CSP vous permet de créer une liste d'autorisation de sources de contenu approuvé et indique au navigateur d'exécuter ou d'afficher uniquement les ressources de ces sources. Même si un pirate informatique parvient à trouver une faille dans laquelle injecter un script, le script 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 faisons de même. Voici un exemple de stratégie qui permet aux scripts de ne s'exécuter que s'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 valide de script et https://apis.google.com comme une autre. Le navigateur peut désormais télécharger et exécuter JavaScript à partir de apis.google.com via HTTPS, ainsi qu'à partir de l'origine de la page actuelle, mais pas à partir d'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é.

Erreur de la console: Vous avez refusé de charger le script "http://evil.example.com/evil.js", car il ne respecte pas la directive suivante de la Content Security Policy: script-src 'self' https://apis.google.com
La console affiche une erreur lorsqu'un script tente de s'exécuter depuis une origine ne figurant pas sur la liste d'autorisation.

La stratégie s'applique à une grande variété de ressources

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 dans l'exemple précédent.

La liste suivante présente les autres directives de ressources à partir du niveau 2. Une spécification de niveau 3 a été rédigée, mais elle n'est en grande partie pas mise en œuvre dans les principaux navigateurs.

base-uri
Limite les URL pouvant apparaître dans l'élément <base> d'une page.
child-src
Répertorie les URL des workers et du contenu des cadres intégrés. Par exemple, child-src https://youtube.com permet d'intégrer des vidéos provenant de YouTube, mais pas de vidéos d'autres origines.
connect-src
Limite les origines auxquelles vous pouvez vous connecter à l'aide de XHR, WebSockets et 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
Liste les points de terminaison valides pour les envois à 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 était obsolète au niveau 2, mais a été restaurée au niveau 3. Dans le cas contraire, 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 de la vidéo et de l'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 en cas de non-respect d'une règle CSP. Cette instruction 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 user-agents 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 devant être réécrites.
worker-src
Directive CSP de niveau 3 qui limite les URL pouvant être chargées en tant que worker, worker partagé ou service worker. Depuis juillet 2017, cette directive possède des implémentations limitées.

Par défaut, le navigateur charge la ressource associée depuis n'importe quelle origine, sans restriction, sauf si vous définissez une règle avec une instruction spécifique. Pour remplacer la valeur par défaut, spécifiez une instruction default-src. Cette directive définit les valeurs par défaut de toute instruction 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 d'instruction font-src, vous ne pouvez charger les polices qu'à partir de https://example.com.

Les instructions suivantes n'utilisent pas default-src comme solution de remplacement. N'oubliez pas que ne pas les définir revient à autoriser quoi que ce soit:

  • base-uri
  • form-action
  • frame-ancestors
  • plugin-types
  • report-uri
  • sandbox

Syntaxe CSP de base

Pour utiliser des directives CSP, répertoriez-les dans l'en-tête HTTP en les séparant par un deux-points. Veillez à répertorier toutes les ressources requises d'un type spécifique dans une seule directive, comme suit:

script-src https://host1.com https://host2.com

Voici un exemple de plusieurs instructions, dans ce cas d'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 encadré ni de plug-ins:

Content-Security-Policy: default-src https://cdn.example.net; child-src 'none'; object-src 'none'

Détails de mise en œuvre

Les navigateurs récents sont compatibles avec 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 devrez envoyer l'en-tête HTTP avec chaque réponse que vous souhaitez protéger. Cela vous permet d'ajuster la règle pour 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, mais pas d'autres, vous pouvez autoriser le chargement du code du bouton uniquement en cas de nécessité.

La liste de sources de chaque directive est flexible. Vous pouvez spécifier les sources par schéma (data:, https:), ou selon leur spécificité, de 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, example.com et uniquement le 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), avec n'importe quel schéma et sur n'importe quel port.

La liste de sources accepte également quatre mots clés:

  • 'none' ne correspond à aucun élément.
  • 'self' correspond à l'origine actuelle, mais pas à ses sous-domaines.
  • 'unsafe-inline' autorise les codes JavaScript et CSS intégrés. Pour en savoir plus, consultez Éviter le code intégré.
  • 'unsafe-eval' autorise les mécanismes texte-vers-JavaScript tels que eval. Pour en savoir plus, consultez Éviter eval().

Ces mots clés doivent être placés entre guillemets simples. Par exemple, script-src 'self' (entre 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 non de l'hôte actuel), ce qui n'est probablement pas ce que vous vouliez dire.

Créez des bacs à sable pour vos pages

Il existe une autre instruction qui mérite d'être abordé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 <iframe> avec un attribut sandbox. Cela peut avoir de nombreux effets sur la page: imposer une origine unique à la page et empêcher l'envoi de formulaires, par exemple. Cela dépasse 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é par les CSP est un en-tête HTTP. Il peut toutefois ê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'">

Impossible d'utiliser 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 intégré. Si un pirate informatique peut injecter un tag de script contenant directement une charge utile malveillante (comme <script>sendMyDataToEvilDotCom()</script>), le navigateur n'a aucun moyen de la distinguer d'un tag de script intégré légitime. CSP résout ce problème en interdisant entièrement les scripts intégrés.

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 ce qui suit à partir de:

<script>
    function doAmazingThings() {
    alert('YOU ARE AMAZING!');
    }
</script>
<button onclick='doAmazingThings();'>Am I amazing?</button>

par un nom plus semblable à celui-ci:

<!-- 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 n'est pas seulement compatible avec CSP, mais également conforme aux bonnes pratiques de conception Web. Le code JavaScript intégré combine structure et comportement de manière à compliquer le code. Elle est également plus compliquée à mettre en cache et à compiler. En déplaçant votre code dans des ressources externes, vous améliorez les performances de vos pages.

Il est également vivement recommandé de déplacer les balises et attributs style intégrés dans des feuilles de style externes pour protéger votre site contre les attaques par exfiltration de données CSS.

Autoriser temporairement les scripts et les 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 directive script-src ou style-src. CSP niveau 2 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 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 de confiance. 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 régénérés pour chaque requête de page, et ils doivent être impossibles à deviner.

Les hachages fonctionnent de manière similaire. Au lieu d'ajouter du code au tag 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 ce qui suit:

<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 CSP accepte également sha384- et sha512-. Lorsque vous générez le hachage, omettez les balises <script>. Les majuscules et les espaces blancs, y compris les espaces de début et de fin, sont importants.

Les solutions permettant de générer des hachages SHA sont disponibles dans un nombre illimité de langues. À l'aide de Chrome 40 ou version ultérieure, vous pouvez ouvrir les outils de développement, puis actualiser votre page. L'onglet "Console" affiche des messages d'erreur avec le hachage SHA-256 approprié pour chacun de vos scripts intégrés.

Éviter la requête eval()

Même lorsqu'un pirate informatique ne peut pas injecter le script directement, il peut tromper votre application pour qu'il convertit le texte d'entrée en 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 par injection de texte. La réponse par défaut de la CSP à ce risque consiste à bloquer complètement tous ces vecteurs.

Cela a les effets suivants sur la façon dont vous créez des applications:

  • Vous devez analyser JSON à l'aide de l'JSON.parse intégré, au lieu d'utiliser eval. Des opérations JSON sécurisées sont disponibles dans tous les navigateurs depuis IE8.
  • Vous devez réécrire tous les appels setTimeout ou setInterval que vous effectuez à l'aide de fonctions intégrées plutôt que de chaînes. Par exemple, si votre page contient les éléments suivants:

    setTimeout("document.querySelector('a').style.display = 'none';", 10);
    

    Réécris-le comme suit:

    setTimeout(function () {
        document.querySelector('a').style.display = 'none';
    }, 10);
      ```
    
  • Évitez les 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 lors de l'exécution, ce qui permet d'évaluer le texte malveillant. Certains frameworks sont compatibles avec CSP par défaut, et recourent à un analyseur robuste en l'absence de eval. La directive ng-csp d'AngularJS en est un bon exemple. Toutefois, nous vous recommandons d'utiliser plutôt 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 la mise en œuvre d'exécution la plus rapide, tout en renforçant la sécurité de votre site.

Si eval() ou d'autres fonctions de texte-vers-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 le déconseillons vivement en raison du risque d'injection de code qu'il présente.

Signaler les cas de non-respect des règles

Pour signaler au serveur les bugs susceptibles de permettre une injection malveillante, vous pouvez indiquer au navigateur de POST signaler les cas 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"
    }
}

Ce rapport contient des informations utiles pour déterminer la cause d'un non-respect des règles, y compris la page sur laquelle le problème s'est produit (document-uri), l'élément referrer de cette page, la ressource qui a enfreint le règlement de la page (blocked-uri), la directive spécifique enfreinte (violated-directive) et le règlement complet de la page (original-policy).

Rapport uniquement

Si vous débutez avec CSP, nous vous recommandons d'utiliser le mode Rapport 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-conformité et corrigez les bugs. Une fois que la nouvelle règle vous convient, commencez à l'appliquer.

Utilisation dans le monde réel

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 basée sur ses exigences. Les sections suivantes présentent quelques cas d'utilisation courants et le processus de décision permettant de les prendre en charge conformément aux consignes relatives aux CSP.

Widgets de réseaux sociaux

  • Le bouton Like (J'aime) de Facebook propose plusieurs options d'implémentation. Nous vous recommandons d'utiliser la version <iframe> pour empêcher son bac à sable du reste de votre site. Elle a besoin d'une instruction child-src https://facebook.com pour fonctionner correctement.
  • Le bouton Tweet de X nécessite l'accès à un script. Déplacez le script fourni dans un fichier JavaScript externe, puis 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 une default-src sur 'none' et de surveiller votre console pour déterminer les ressources que vous devez 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 une CSP pour un site bancaire, en commençant par une règle 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. Elle utilise des cadres, mais uniquement pour les pages locales du site (pas d'origines tierces). Il n'y a pas de contenu Flash sur le site, pas de polices, pas d'extras. 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 des logiciels de forum tiers remplis 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 instructions de script et de style n'héritent pas automatiquement de cette source. Chaque directive remplace la valeur par défaut pour ce type de ressource spécifique.

Développement de la norme CSP

Content Security Policy Level 2 est une norme recommandée du W3C. Le groupe de travail du W3C sur la sécurité des applications Web développe la prochaine itération de la spécification, Content Security Policy Level 3.

Pour suivre la discussion sur ces fonctionnalités à venir, consultez les archives de la liste de diffusion public-webappsec@.