Magentix

Développeur Magento indépendant depuis 2009

Billets : Utiliser une stratégie de sécurité du contenu (Content Security Policy)


Utiliser une stratégie de sécurité du contenu (Content Security Policy)

Le CSP (Content Security Policy) est un excellent moyen de diminuer les attaques XSS et les injections de contenu. Vous décidez des resources internes et externes que le navigateur a le droit de charger. Configurer une stratégie CSP nécessite simplement d'utiliser un en-tête HTTP Content-Security-Policy.

La mise en place d'une stratégie de sécurité du contenu avec CSP est extrêmement simple et efficace. CSP vous permet d'éliminer les moyens de réaliser des attaques XSS en permettant de spécifier les domaines autorisés à fournir des resources (scripts, images, CSS...) pour la page visitée.

Configuration

Plusieurs possibilités pour inclure l'en-tête Content-Security-Policy.

Configuration Apache

# Content-Security-Policy
<IfModule mod_headers.c>
    Header set Content-Security-Policy "default-src 'self'"
</IfModule>
# /Content-Security-Policy

Configuration Nginx

add_header Content-Security-Policy "default-src 'self';"

Fichier .htaccess

# Content-Security-Policy
<IfModule mod_headers.c>
    Header set Content-Security-Policy "default-src 'self'"
</IfModule>
# /Content-Security-Policy

Header (PHP)

header("Content-Security-Policy: default-src 'self'");

Meta Tag

<meta http-equiv="Content-Security-Policy" content="default-src 'self'" />

Page d'exemple

Pour notre article, nous utilisons la page suivante :

<!doctype html>
<html lang="fr">
    <head>
        <title>Content Security Policy</title>

        <!-- Content Security Policy -->
        <meta http-equiv="Content-Security-Policy" content="default-src 'self'" />

        <!-- Local resources -->
        <link rel="stylesheet" href="css/style.css" type="text/css" />
        <script type="text/javascript" src="js/script.js"></script>

        <!-- External resources -->
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.10/styles/default.min.css" type="text/css" />
        <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.10/highlight.min.js"></script>

        <!-- Inline CSS -->
        <style>
            h1 {
                color:#CC0000
            }
        </style>
    </head>
    <body>
        <h1>CSP Page</h1>

        <!-- Inline CSS -->
        <p style="font-weight: bold">Content Security Policy example page</p>

        <!-- External image -->
        <img src="https://fr.wikipedia.org/static/images/project-logos/frwiki.png" alt="Wikipedia" />

        <!-- Inline Script -->
        <script type="text/javascript">
            console.log('ok');
        </script>
    </body>
</html>

Application

Si vous copiez la page d'exemple dans un fichier html, la majorité des resources sont bloquées. Vous obtenez dans la console du navigateur :

Blocage des resources par le navigateur (CSP)

Resources locales

Considérons la directive CSP suivante :

default-src 'self'

Nous indiquons ici au navigateur de ne charger que les resources locales, c'est à dire les fichiers Javascript et CSS herbergés sur notre serveur. Dans notre page d'exemple cela correspond à :

<!-- Local resources -->
<link rel="stylesheet" href="css/style.css" type="text/css" />
<script type="text/javascript" src="js/script.js"></script>

Cette stratégie est la plus restrictive et sécurisée qui soit (après la valeur "none" mais c'est un peu excessif). Par défaut nous n'autorisons que les resources locales, les scripts et CSS inline sont ignorés.

Style inline

Notre page contient des styles en ligne dans le header et dans la page. Avec la directive précedente ils sont ignorés. Nous pouvons autoriser le style inline grâce à la valeur unsafe-inline de la directive style-src :

default-src 'self'; style-src 'self' 'unsafe-inline'

Avec cette stratégie, nos styles en ligne sont maintenant interprétés :

<!-- Inline CSS -->
<style>
    h1 {
        color:#CC0000
    }
</style>
<!-- Inline CSS -->
<p style="font-weight: bold">Content Security Policy example page</p>

Script inline

Notre page contient un script en ligne ignoré avec notre politique par défaut. Il est cependant possible de d'accepter les scripts inline par le biais de la valeur unsafe-inline de la directive script-src :

default-src 'self'; script-src 'self' 'unsafe-inline'

Nous autorisons ainsi la portion de code suivante :

<!-- Inline Script -->
<script type="text/javascript">
    console.log('ok');
</script>

Dans la pratique il est souvent complexe de ne pas autoriser les scripts en ligne, mais pour une stratégie de sécurité du contenu optimale il faut tendre à ne plus les utiliser.

CSS externe

Nous tentons de charger une resource CSS herbergée sur un serveur distant (cdnjs.cloudflare.com). Cette resource est automatiquement bloquée. Il est nécessaire de l'autoriser. Il faut pour cela indiquer le domaine concerné pour style-src :

default-src 'self'; style-src 'self' cdnjs.cloudflare.com

Cette stratégie nous permet de charger le CSS externe spécifié dans le header :

<!-- External resources -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/.../styles/default.min.css" type="text/css" />

Script externe

Nous souhaitons charger un Script herbergé sur un serveur distant (cdnjs.cloudflare.com). Cette resource est bloquée par défaut. Il nous faut l'autoriser :

default-src 'self'; script-src 'self' cdnjs.cloudflare.com

Cette stratégie nous permet de charger le Script spécifié dans le header :

<!-- External resources -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax.../highlight.min.js"></script>

Image externe

Pour finir, une image externe doit apparaître dans la page. Elle est également bloquée par défaut. Nous l'autorisons via img-src :

default-src 'self'; img-src 'self' fr.wikipedia.org

Nous acceptons ainsi l'affichage des images issues du domaine fr.wikipedia.org :

<img src="https://fr.wikipedia.org/static/images/project-logos/frwiki.png" alt="Wikipedia" />

Autres directives

Dans notre page d'exemple nous avons abordés les directives CSP (version 1) suivantes :

CSP présente d'autres directives à utiliser selon vos besoins :

Toutes les directive acceptent les valeurs none, self et une liste de domaine.

Les directives style-src et script-src acceptent également la valeur unsafe-inline. Il est possible d'indiquer unsafe-eval avec script-src pour autoriser l'utilisation de la méthode eval().

La directive img-src accepte data: pour les images en base64 (script-src 'self' data:)

D'autres directives existent en CSP version 2 et 3 (form-action, child-src...). Retrouvez l'ensemble des directives sur MDN Web Docs.

Stratégie de sécurité

Pour afficher complétement notre page d'exemple, nous arrivons finallement à la stratégie de sécurité suivante :

default-src 'self'; style-src 'self' 'unsafe-inline' cdnjs.cloudflare.com; script-src 'self' 'unsafe-inline' cdnjs.cloudflare.com; img-src 'self' fr.wikipedia.org

Cette stratégie d'exemple n'est évidemment pas la plus sécurisée. Il est conseillé d'éviter les styles et scripts "inline" ainsi que l'utilisation de resources externes, cela peut souvent être évité.

La mise en place de CSP sur un site existant peut donc être extrêmement complexe, une politique moins restrictive (unsafe-inline) sera certainement nécessaire. L'avantage est qu'une fois établie vous maîtrisez les resources externes et le respect des bonnes pratiques pour les intégrations futurs.

Dans tous les cas n'oubliez jamais de vérifier la console du navigateur pour adapter si besoin votre stratégie de sécurité du contenu lors de sa mise en place.

Google Analytics

Si comme une grande majorité de sites vous implémentez Google Analytics, vous pouvez appliquer la stratégie de base suivante :

default-src 'self'; script-src 'self' 'unsafe-inline' www.googletagmanager.com www.google-analytics.com; img-src 'self' www.google-analytics.com