L’auteur a sélectionné Girls Who Code pour recevoir un don dans le cadre du programme Write for DOnations.
Introduction
Lorsque vous visitez un site Web, diverses ressources sont utilisées pour le charger et l’afficher. Par exemple, lorsque vous allez sur https://www.digitalocean.com
, votre navigateur télécharge le HTML et le CSS directement depuis digitalocean.com
. Cependant, les images et autres éléments sont téléchargés depuis assets.digitalocean.com
, et les scripts d’analyse sont chargés à partir de leurs domaines respectifs.
Certains sites Web utilisent une multitude de services, de styles et de scripts différents pour charger et afficher leur contenu, et votre navigateur exécutera tout cela. Un navigateur ne sait pas si le code est malveillant, il est donc de la responsabilité du développeur de protéger les utilisateurs. Étant donné qu’il peut y avoir de nombreuses ressources sur un site Web, avoir une fonctionnalité dans le navigateur qui ne permet que les ressources approuvées est un bon moyen de garantir que les utilisateurs ne soient pas compromis. C’est à cela que servent les politiques de sécurité du contenu (CSP).
En utilisant un en-tête CSP, un développeur peut autoriser explicitement certaines ressources à s’exécuter tout en empêchant toutes les autres. Étant donné que la plupart des sites peuvent avoir plus de 100 ressources, et que chacune doit être approuvée pour la catégorie spécifique de ressource qu’elle est, mettre en œuvre un CSP peut être une tâche fastidieuse. Cependant, un site Web avec un CSP sera plus sécurisé car il garantit que seules les ressources approuvées sont autorisées à s’exécuter.
Dans ce tutoriel, vous allez mettre en œuvre un CSP dans une application Django de base. Vous allez personnaliser le CSP pour autoriser certains domaines et ressources inline à s’exécuter. En option, vous pouvez également utiliser Sentry pour enregistrer les violations.
Prérequis
Pour mener à bien ce tutoriel, vous aurez besoin de :
- A working Django project (version 3 or greater is preferred), either on your local machine or a DigitalOcean Droplet. If you don’t have one, you can create one with the tutorial, How to Install Django and Set Up a Development Environment on Ubuntu 20.04.
- A web browser like Firefox or Chrome and an understanding of browser network tools. For more on using browser network tools, check out the product documentation for the Network Monitor in Firefox or the DevTools Network Tab in Chrome. For more general guidance on browser developer tools, see the guide: What are Browser Developer Tools?
- Connaissance de Python 3 et Django, que vous pouvez acquérir grâce à la série de tutoriels, Comment Coder en Python et Développement Django.
- Un compte sur Sentry pour suivre les violations CSP (en option).
Étape 1 — Création d’une vue de démonstration
Dans cette étape, vous allez modifier la façon dont votre application gère les vues afin de pouvoir ajouter le support CSP.
Comme prérequis, vous avez installé Django et configuré un projet d’exemple. La vue par défaut dans Django est trop simple pour démontrer toutes les capacités du middleware CSP, vous allez donc créer une page HTML simple pour ce tutoriel.
Naviguez vers le dossier du projet que vous avez créé dans les prérequis :
Pendant que vous êtes dans le répertoire django-apps
, créez votre environnement virtuel. Nous le nommerons env
générique, mais vous devriez utiliser un nom qui a du sens pour vous et votre projet.
Ensuite, activez l’environnement virtuel avec la commande suivante :
Dans l’environnement virtuel, créez un fichier views.py
dans votre dossier de projet en utilisant nano
, ou votre éditeur de texte préféré :
Ensuite, ajoutez une vue de base qui rendra un modèle index.html
que vous créerez ensuite. Ajoutez ce qui suit à views.py
:
Enregistrez et fermez le fichier lorsque vous avez terminé.
Créez un modèle index.html
dans un nouveau répertoire templates
:
Ajoutez ce qui suit à index.html
:
La vue que nous avons créée rendra cette page HTML simple. Elle affichera le texte Bonjour, Sammy ! ainsi qu’une image de Sammy le requin.
Enregistrez et fermez le fichier lorsque vous avez terminé.
Pour accéder à cette vue, vous devrez mettre à jour urls.py
:
Importez le fichier views.py
et ajoutez une nouvelle route en ajoutant les lignes surlignées :
La nouvelle vue que vous venez de créer sera maintenant visible lorsque vous visiterez /
(lorsque l’application est en cours d’exécution).
Enregistrez et fermez le fichier.
Enfin, vous devrez mettre à jour INSTALLED_APPS
pour inclure testsite
dans settings.py
:
Voici, vous ajoutez testsite
à la liste des applications dans settings.py
afin que Django puisse faire certaines hypothèses sur la structure de votre projet. Dans ce cas, il supposera que le dossier templates
contient des templates Django que vous pouvez utiliser pour rendre des vues.
À partir du répertoire racine du projet (testsite
), lancez le serveur de développement Django avec la commande suivante, en remplaçant l'adresse IP de votre serveur
par votre propre adresse IP.
Ouvrez un navigateur et visitez l'adresse IP de votre serveur:8000
. La page devrait ressembler à ceci :
À ce stade, la page affiche une photo de profil de Sammy le requin. Sous l’image se trouve le texte Bonjour, Sammy ! en script bleu.
Pour arrêter le serveur de développement Django, appuyez sur CONTROL-C
.
Dans cette étape, vous avez créé une vue de base qui agit comme la page d’accueil de votre projet Django. Ensuite, vous ajouterez le support CSP à votre application.
Étape 2 — Installation du Middleware CSP
Dans cette étape, vous installerez et implémenterez un middleware CSP afin de pouvoir ajouter des en-têtes CSP et travailler avec des fonctionnalités CSP dans vos vues. Le middleware ajoute des fonctionnalités supplémentaires à toutes les requêtes ou réponses que Django gère. Dans ce cas, le Middleware Django-CSP ajoute le support CSP aux réponses Django.
Tout d’abord, vous installerez le Middleware CSP de Mozilla dans votre projet Django en utilisant pip
, le gestionnaire de paquets Python. Utilisez la commande suivante pour installer le paquet nécessaire depuis PyPi, l’Index des packages Python. Pour exécuter la commande, vous pouvez soit arrêter le serveur de développement Django en utilisant CONTROL-C
, soit ouvrir un nouvel onglet dans votre terminal :
Ensuite, ajoutez le middleware aux paramètres de votre projet Django. Ouvrez settings.py
:
Avec django-csp
installé, vous pouvez maintenant ajouter le middleware dans settings.py
. Cela ajoutera des en-têtes CSP à vos réponses.
Ajoutez la ligne suivante au tableau de configuration MIDDLEWARE
:
Enregistrez et fermez le fichier lorsque vous avez terminé. Votre projet Django prend désormais en charge les CSP. À l’étape suivante, vous commencerez à ajouter des en-têtes CSP.
Étape 3 — Implémentation d’un en-tête CSP
Maintenant que votre projet prend en charge les CSP, il est prêt à être sécurisé. Pour ce faire, vous configurerez le projet pour ajouter des en-têtes CSP à vos réponses. Un en-tête CSP est ce qui indique au navigateur comment se comporter lorsqu’il rencontre un type de contenu particulier. Ainsi, si l’en-tête dit de n’autoriser que les images provenant d’un domaine particulier, alors le navigateur n’autorisera que les images de ce domaine.
À l’aide de nano ou de votre éditeur de texte préféré, ouvrez settings.py
:
Définissez les variables suivantes n’importe où dans le fichier :
Ces règles sont la base de votre CSP. Ces lignes indiquent quelles sources sont autorisées pour les images, les feuilles de style et les scripts, respectivement. Pour l’instant, elles contiennent toutes la chaîne 'self'
, ce qui signifie que seules les ressources de votre propre domaine sont autorisées.
Enregistrez et fermez le fichier lorsque vous avez terminé.
Exécutez votre projet Django avec la commande suivante :
Lorsque vous visitez votre-adresse-ip-de-serveur:8000
, vous verrez que le site est cassé :
Comme prévu, l’image n’apparaît pas et le texte apparaît dans le style par défaut (gras noir). Cela signifie que l’en-tête CSP est appliqué, et notre page est maintenant plus sécurisée. Parce que la vue que vous avez créée précédemment fait référence à des feuilles de style et des images à partir de domaines qui ne sont pas les vôtres, le navigateur les bloque.
Votre projet a maintenant un CSP fonctionnel qui indique au navigateur de bloquer les ressources qui ne proviennent pas de votre domaine. Ensuite, vous allez modifier le CSP pour autoriser des ressources spécifiques, ce qui corrigera l’image manquante et le style sur la page d’accueil.
Étape 4 — Modification du CSP pour autoriser les ressources externes
Maintenant que vous avez un CSP de base, vous allez le modifier en fonction de ce que vous utilisez sur votre site. Par exemple, un site web qui utilise Adobe Fonts et des vidéos YouTube intégrées devra autoriser ces ressources. Cependant, si votre site web ne fait qu’afficher des images provenant de votre propre domaine, vous pouvez laisser les paramètres des images avec leurs valeurs par défaut restrictives.
La première étape consiste à trouver chaque ressource que vous devez approuver. Vous pouvez utiliser les outils de développement de votre navigateur à cette fin. Ouvrez le Moniteur réseau dans l’Inspecteur, rafraîchissez la page et examinez les ressources bloquées :
Le journal du réseau montre que deux ressources sont bloquées par le CSP : une feuille de style provenant de fonts.googleapis.com
et une image provenant de html.sammy-codes.com
. Pour autoriser ces ressources dans l’en-tête CSP, vous devrez modifier les variables dans settings.py
.
Pour autoriser les ressources provenant de domaines externes, ajoutez le domaine à la partie du CSP qui correspond au type de fichier. Ainsi, pour autoriser une image provenant de html.sammy-codes.com
, vous ajouterez html.sammy-codes.com
à CSP_STYLE_SRC
.
Ouvrez settings.py
et ajoutez ce qui suit à la variable CSP_STYLE_SRC
:
Maintenant, au lieu de n’autoriser que les images de votre domaine, le site autorise également les images de html.sammy-codes.com
.
La vue d’index utilise Google Fonts. Google fournit à votre site les polices (à partir de https://fonts.gstatic.com
) et une feuille de style pour les appliquer (à partir de https://fonts.googleapis.com
). Pour permettre le chargement des polices, ajoutez ce qui suit à votre CSP :
De la même manière que vous autorisez les images provenant de html.sammy-codes.com
, vous autoriserez également les feuilles de style provenant de fonts.googleapis.com
et les polices provenant de fonts.gstatic.com
. Pour information, la feuille de style chargée depuis fonts.googleapis.com
est utilisée pour appliquer les polices. Les polices elles-mêmes sont chargées depuis fonts.gstatic.com
.
Enregistrez et fermez le fichier.
Avertissement : Tout comme self
, il existe d’autres mots-clés tels que unsafe-inline
, unsafe-eval
ou unsafe-hashes
qui peuvent être utilisés dans un CSP. Il est fortement recommandé d’éviter d’utiliser ces règles dans votre CSP. Bien que cela facilite la mise en œuvre, ils peuvent être utilisés pour contourner le CSP et le rendre inutile.
Pour plus d’informations, consultez la documentation produit Mozilla pour « Script inline non sécurisé ».
Maintenant, Google Fonts pourra charger les styles et les polices sur votre site et html.sammy-codes.com
pourra charger des images. Cependant, lorsque vous visitez une page sur votre serveur, vous remarquerez peut-être que seules les images se chargent maintenant. C’est parce que les styles intégrés dans le HTML qui sont utilisés pour appliquer les polices ne sont pas autorisés. Vous corrigerez cela dans la prochaine étape.
Étape 5 — Travailler avec les Scripts et Styles Inline
À ce stade, vous avez modifié le CSP pour autoriser les ressources externes. Mais les ressources inline, telles que les styles et les scripts dans votre vue, ne sont toujours pas autorisées. À cette étape, vous allez les faire fonctionner afin de pouvoir appliquer le style de police.
Il existe deux façons d’autoriser les scripts et les styles inline : les nonces et les hachages. Si vous constatez que vous modifiez fréquemment les scripts et styles inline, utilisez des nonces pour éviter des modifications fréquentes de votre CSP. Si vous mettez rarement à jour les scripts et styles inline, l’utilisation de hachages est une approche raisonnable.
Utilisation de nonce
pour autoriser les Scripts Inline
Tout d’abord, vous utiliserez l’approche du nonce. Un nonce est un jeton généré de manière aléatoire qui est unique à chaque demande. Si deux personnes visitent votre site, elles recevront chacune un nonce
unique qui est incorporé dans les scripts et styles inline que vous approuvez. Pensez au nonce comme à un mot de passe unique qui approuve certaines parties d’un site pour une seule session.
Pour ajouter le support du nonce à votre projet, vous mettrez à jour votre CSP dans settings.py
. Ouvrez le fichier pour le modifier :
Ajoutez script-src
dans CSP_INCLUDE_NONCE_IN
dans le fichier settings.py
.
Définissez CSP_INCLUDE_NONCE_IN
n’importe où dans le fichier et ajoutez 'script-src'
à celui-ci :
CSP_INCLUDE_NONCE_IN
indique à quels scripts en ligne vous êtes autorisé à ajouter des attributs nonce
. CSP_INCLUDE_NONCE_IN
est traité comme un tableau car plusieurs sources de données prennent en charge les nonces (par exemple, style-src
).
Enregistrez et fermez le fichier.
Les nonces sont désormais autorisés à être générés pour les scripts en ligne lorsque vous ajoutez l’attribut nonce
à ceux-ci dans votre modèle de vue. Pour tester cela, vous utiliserez un simple extrait de code JavaScript.
Ouvrez index.html
pour l’éditer :
Ajoutez l’extrait suivant dans la balise <head>
du HTML :
Cet extrait imprime Bonjour depuis la console !
dans la console du navigateur. Cependant, comme votre projet a une CSP qui n’autorise que les scripts en ligne s’ils ont un nonce
, ce script ne s’exécutera pas et produira plutôt une erreur.
Vous pouvez voir cette erreur dans la console de votre navigateur lorsque vous rafraîchissez la page :
L’image se charge car vous avez autorisé les ressources externes à l’étape précédente. Comme prévu, le style est actuellement par défaut car vous n’avez pas encore autorisé les styles en ligne. Également comme prévu, le message de la console n’a pas été affiché et a renvoyé une erreur. Vous devrez lui donner un nonce
pour l’approuver.
Vous pouvez le faire en ajoutant nonce="{{request.csp_nonce}}"
à ce script en tant qu’attribut. Ouvrez index.html
pour l’édition et ajoutez la portion surlignée comme indiqué ici :
Enregistrez et fermez votre fichier lorsque vous avez terminé.
Si vous actualisez la page, le script s’exécutera maintenant :
Lorsque vous regardez dans Inspecter l’élément, vous remarquerez qu’il n’y a pas de valeur pour l’attribut :
La valeur n’apparaît pas pour des raisons de sécurité. Le navigateur a déjà traité la valeur. Elle est cachée de sorte que tout script ayant accès au DOM ne peut pas y accéder et l’appliquer à un autre script. Si vous affichez la source de la page à la place, voici ce que le navigateur a reçu :
Remarquez qu’à chaque fois que vous actualisez la page, la valeur nonce
change. Cela est dû au fait que le middleware CSP de notre projet génère un nouveau nonce
pour chaque requête.
Ces valeurs de nonce
sont ajoutées à l’en-tête CSP lorsque le navigateur reçoit la réponse :
Chaque demande que le navigateur fait à votre site aura une valeur nonce
unique pour ce script. Puisque le nonce
est fourni dans l’en-tête CSP, cela signifie que le serveur Django a approuvé l’exécution de ce script spécifique.
Vous avez mis à jour votre projet pour fonctionner avec nonce, qui peut être appliqué à plusieurs ressources. Par exemple, vous pouvez également l’appliquer aux styles, en mettant à jour CSP_INCLUDE_NONCE_IN
pour permettre style-src
. Mais il existe une approche plus simple pour approuver les ressources en ligne, et c’est ce que vous ferez ensuite.
Utilisation des hachages pour autoriser les styles en ligne
Une autre approche pour permettre les scripts et styles en ligne est avec les hachages. Un hachage est un identifiant unique pour une ressource en ligne donnée.
À titre d’exemple, voici le style en ligne dans notre modèle:
Actuellement, cependant, les styles ne fonctionnent pas. Lorsque vous visualisez le site dans le navigateur, les images se chargent avec succès, mais les polices et les styles ne sont pas appliqués:
Dans la console du navigateur, vous trouverez une erreur indiquant qu’un style en ligne viole la CSP. (Il peut y avoir d’autres erreurs, mais recherchez l’erreur concernant le style en ligne.)
L’erreur est produite car le style n’est pas approuvé par notre CSP. Mais remarquez que l’erreur fournit le hachage nécessaire pour approuver l’extrait de style. Ce hachage est unique à cet extrait de style spécifique. Aucun autre extrait n’aura jamais le même hachage. Lorsque ce hachage est placé à l’intérieur de la CSP, chaque fois que ce style spécifique est chargé, il sera approuvé. Mais si vous modifiez ces styles, vous devrez obtenir le nouveau hachage et remplacer l’ancien par celui-ci dans la CSP.
Vous allez maintenant appliquer le hachage en l’ajoutant à CSP_STYLE_SRC
dans settings.py
, comme ceci:
Ajouter le hachage sha256-...
à la liste CSP_STYLE_SRC
permettra au navigateur de charger la feuille de style sans erreur.
Enregistrez et fermez le fichier.
Maintenant, rechargez le site dans le navigateur, et les polices et les styles devraient se charger avec succès :
Les styles et scripts en ligne fonctionnent maintenant correctement. À cette étape, vous avez utilisé deux approches différentes, les nonces et les hachages, pour permettre les styles et scripts en ligne.
Mais, il y a un problème important à résoudre. Les CSP sont fastidieux à maintenir, surtout pour les gros sites web. Vous pouvez avoir besoin d’un moyen de suivre quand le CSP bloque une ressource afin de déterminer s’il s’agit d’une ressource malveillante ou simplement d’une partie cassée de votre site. À l’étape suivante, vous utiliserez Sentry pour journaliser et suivre toutes les violations produites par votre CSP.
Étape 6 — Signalement des violations avec Sentry (Facultatif)
Étant donné à quel point les CSP ont tendance à être stricts, il est bon de savoir quand ils bloquent du contenu — surtout puisque bloquer du contenu signifie probablement qu’une certaine fonctionnalité sur votre site ne fonctionnera pas. Des outils comme Sentry peuvent vous informer lorsque le CSP bloque des requêtes pour les utilisateurs. À cette étape, vous configurerez Sentry pour journaliser et signaler les violations du CSP.
Comme prérequis, vous avez créé un compte avec Sentry. Maintenant, vous allez créer un projet.
Dans le coin supérieur gauche du tableau de bord de Sentry, cliquez sur l’onglet Projets:
Dans le coin supérieur droit, cliquez sur le bouton Créer un projet:
Vous verrez plusieurs logos avec un titre indiquant Choisissez une plateforme. Choisissez Django:
Ensuite, en bas, nommez votre projet (pour cet exemple, nous utiliserons sammys-tutorial
), et cliquez sur le bouton Créer le projet:
Sentry vous fournira un extrait de code à ajouter à votre fichier settings.py
. Enregistrez cet extrait pour l’ajouter à une étape ultérieure.
Dans votre terminal, installez le SDK de Sentry:
Ouvrez settings.py
comme ceci:
Ajoutez ce qui suit à la fin du fichier, et assurez-vous de remplacer SENTRY_DSN
par la valeur du tableau de bord:
Ce code est fourni par Sentry afin de pouvoir enregistrer toutes les erreurs survenant dans votre application. Il s’agit de la configuration par défaut pour Sentry et initialise Sentry pour l’enregistrement des problèmes sur notre serveur. Techniquement, vous n’avez pas besoin d’initialiser Sentry sur votre serveur pour les violations de CSP, mais dans le cas rare où il y aurait un problème avec le rendu des nonces ou des hachages, ces erreurs seront enregistrées dans Sentry.
Enregistrez et fermez le fichier.
Ensuite, retournez au tableau de bord de votre projet et cliquez sur l’icône d’engrenage pour accéder aux Paramètres:
Allez à l’onglet En-têtes de sécurité:
Copiez le report-uri
:
Ajoutez-le à votre CSP comme ceci :
Assurez-vous de remplacer votre-report-uri
par la valeur que vous avez copiée depuis le tableau de bord.
Enregistrez et fermez votre fichier. Maintenant, lorsque l’application de la CSP entraîne une violation, Sentry la consignera dans cette URI. Vous pouvez essayer ceci en supprimant un domaine ou un hachage de votre CSP, ou en supprimant le nonce
du script que vous avez ajouté précédemment. Chargez la page dans le navigateur et vous verrez l’erreur dans la page Problèmes de Sentry :
Si vous vous trouvez submergé par le nombre de journaux, vous pouvez également définir
CSP_REPORT_PERCENTAGE
dans settings.py
pour ne envoyer qu’un pourcentage des journaux à Sentry.
Maintenant, chaque fois qu’il y a une violation de la CSP, vous serez notifié et pourrez voir l’erreur dans Sentry.
Conclusion
Dans cet article, vous avez sécurisé votre application Django avec une politique de sécurité du contenu. Vous avez mis à jour votre politique pour autoriser les ressources externes et utilisez des nonces et des hachages pour permettre les scripts et les styles en ligne. Vous l’avez également configuré pour envoyer les violations à Sentry. Comme prochaine étape, consultez la documentation de Django CSP pour en savoir plus sur la façon de faire respecter votre CSP.