Logo

Tous publics
Afficher seulement les articles tous publics
Public technique
Affichement seulement les articles techniques

Comprendre et implémenter Content-Security-Policy

Benjamin 08 mars 2019 Public technique

Lorsque nous faisons des audits web pour nos clients, nous relevons parfois que certains entêtes de sécurité sont manquants dans les réponses du serveur web : X-Content-Type-Options, X-Frame-Options, X-XSS-Protection... Toutefois, un entête dont nous constatons systématiquement l'absence est Content-Security-Policy. Nous pensons que cet état de fait est dû à la fois à la relative "jeunesse" de cet entête, mais aussi sa complexité de mise en place par rapport aux autres.

Nous allons dans cet article tenter de faire ressortir l'impact très positif que peut avoir cet entête sur la sécurité des sites web, et détailler la manière de la paramétrer afin, nous l'espérons, de faciliter son adoption.

CSP, kézako ?

Content-Security-Policy est un entête de sécurité HTTP dont la première spécification date de février 2015, mise à jour par une deuxième version en décembre 2016, et toujours en cours d'évolution via une troisième version. Concrètement, cet entête renvoyé par le serveur indique aux navigateurs des utilisateurs quelles sources sont autorisées à charger du contenu sur le site concerné.

Prenons un cas simple : vous faites l'inclusion sur votre site de polices d'écriture particulières via un service externe. De plus, vous utilisez un service de mesure d'audience externe. En paramétrant correctement cet entête, vous allez pouvoir indiquer que le navigateur des utilisateurs est autorisé à charger sur votre site des polices d'écriture et des scripts depuis les serveurs de Google (et le vôtre, si nécessaire), mais pas depuis d'autres serveurs.

De quelles menaces concrètes cet entête protège-t-il ?

Premier cas : les failles Cross-Site Scripting

Les failles Cross-Site Scripting (ou XSS) proviennent d'un manque de filtrage des saisies utilisateurs. Elles permettent à un attaquant de charger du code Javascript qui s'exécutera dans le navigateur des utilisateurs.

Le code malicieux exécuté peut avoir divers objectifs : entres autres, récupérer toutes les saisies utilisateurs et les envoyer sur le serveur de l'attaquant (nom d'utilisateur, mot de passe, données personnelles, numéro de carte bancaire...) ou miner des cryptomonnaies en utilisant les ressources de l'ordinateur des utilisateurs (cette attaque s'appelle le cryptojacking).

Lorsqu'il découvre une faille XSS, un attaquant va tenter d'infecter un maximum d'utilisateurs. Pour cela, il va écrire du code qui soit sera injecté en une seule fois directement dans le site web vulnérable (on parle de script inline), soit il sera chargé depuis son propre site, donc depuis une adresse IP ou un nom de domaine différent du site web vulnérable.

Dans ce cas, Content-Security-Policy permet d'empêcher un attaquant de charger son script sur la page, qu'il soit inline ou hébergé sur son propre serveur, puisqu'il n'aura pas été explicitement approuvé dans la politique.

Deuxième cas : chargement indélicat de publicités

Il a été observé que certaines entités ont injecté du code Javascript au sein de sites web légitimes, et pourtant non vulnérables (à priori), à fin de servir leurs propres intérêts. Comment ? Ces entités fournissent en fait un accès Internet à leurs clients, et ont pris la liberté de modifier à la volée les sites web consultés.

Il s'agit par exemple du Fournisseur d'Accès Internet américain Comcast, qui injectait du code Javascript de plusieurs centaines de lignes dans les pages consultées par ses visiteurs afin de promouvoir ses équipements type modem et routeurs. Selon la société, il s'agissait d'avertir les utilisateurs qu'ils utilisaient un modem obsolète.

La situation s'est produite sur la connexion WiFi fournie par certains hôtels Marriott à ses clients, ou encore sur la connexion WiFi fournie par AT&T à certains aéroports. Dans les deux cas, la raison invoquée était de trouver des sources de revenus permettant de financer le réseau WiFi aux usagers.

Là encore, Content-Security-Policy permet d'empêcher des sociétés et prestataires de charger leur propre contenu à la volée sur une page, puisqu'il n'aura pas été explicitement approuvé dans la politique.

Troisième cas : librairie externe attaquée

De plus en plus de librairies externes sont hébergées sur des CDN (Content Delivery Networks) et chargées depuis ces serveurs sur les sites qui en font l'usage. On pense notamment à jQuery, Bootstrap ou encore FontAwesome, pour les plus connues. Ces scripts sont donc chargés sur des milliers de sites, qui choisissent d'utiliser ces CDN plutôt que de le servir à leurs utilisateurs depuis leur propre serveur. Cela permet de réduire la bande-passante et d'accélérer les temps de chargement.

Les attaquants y ont vu une occasion incroyable : attaquer un seul serveur, celui qui héberge le code source des librairies, permet de compromettre les milliers de serveurs qui l'utilisent. Cela s'est produit courant 2018, lorsque plusieurs librairies Javascript chargées entre autres par TicketMaster (et près d'un millier de sites de e-commerce) ont été attaquées par le groupe d'attaquants Magecart. Les clients de ces sites e-commerce se sont fait dérober leurs données personnelles et de paiement.

Le chercheur en sécurité Scott Helme a quant à lui constaté le même problème sur de nombreux sites Internet, notamment le site des Cours fédérales américaines, le site de la Sécurité Sociale anglaise, ou encore le site d'un département d'Australie. Tous ces sites chargeaient la librairie BrowseAloud, qui permet aux malvoyants de lire les contenus web, et qui s'est faite attaquer par un groupe d'attaquants. Le but était cette fois de miner des cryptomonnaies sur les ordinateurs des visiteurs.

Dans ce cas particulier, Content-Security-Policy aurait aidé à se protéger, mais n'aurait pas forcément été suffisant. Si l'attaquant utilise la librairie pour charger un fichier se trouvant sur son propre serveur, Content-Security-Policy empêche le code situé sur le serveur de l'attaquant de se charger. En revanche, si l'attaquant compromet la librairie en ajoutant son propre code dans la librairie, Content-Security-Policy utilisé seul est inefficace : il faut alors utiliser Sub-Resource Integrity.

Comment définir une Content-Security-Policy ?

L’objectif de la première étape est de connaître tous les services externes que vous utilisez : régies publicitaires, outils de mesures d'audiences, librairies Javascript, polices d'écritures, images, services de cartographie, etc. Il convient de lister, pour chacun de ces services, le type de ressource chargée, ainsi que les noms de domaine qui hébergent le contenu chargé.

Par exemple, dans le cas de notre site vitrine (algosecure.fr), cette cartographie des services externes peut se décliner de la manière suivante :

Service Type de ressource Nom de domaine
Mapbox Images, scripts et données de la carte interactive de notre page Contact *.mapbox.com
Notre propre instance de Matomo Scripts et images pour la mesure d'audience nostats.algosecure.fr
YouTube Une iframe, pour afficher une vidéo www.youtube.com
Le site de l'université de Carnegie-Mellon Une image, pour afficher le logo CERT www.sei.cmu.edu

La seconde étape a pour objectif de construire la politique. Il s'agit d'indiquer, pour chaque type de contenu, les sources autorisées. Nous allons également ajouter :

  • une instruction qui permet, pour tous les types non-définis dans notre politique (images, scripts et iframes), d'autoriser le chargement depuis notre propre nom de domaine (c'est à dire 'self'),
  • une instruction qui permet de journaliser les erreurs de chargement.

C'est en effet l'une des fonctionnalités bien pensées de la CSP : si du fait d'un mauvais paramétrage, votre politique venait à bloquer des ressources légitimes dont vous auriez souhaité autoriser le chargement, ces blocages seront journalisés afin de déterminer précisément quelle source a essayé de charger du contenu et quelle instruction de votre politique a été enfreinte, afin de vous aider à améliorer votre CSP. Le site Report-URI permet à cet effet de récolter ces journaux et vous les présenter de manière intelligible. Le service est gratuit pour les "petits" sites, et avec des tarifs progressifs pour les sites plus importants.

Par ailleurs, la CSP possède un mode nommé "Report Only", qui permet dans un premier temps de ne pas bloquer les ressources qui n'auraient pas été autorisées dans la CSP. Seuls les rapports vous seront envoyés, et aucun ressource ne sera bloquée dans la navigateur des clients.

Finalement, notre politique est donc la suivante :

Content-Security-Policy-Report-Only "default-src 'self' nostats.algosecure.fr data: *.mapbox.com www.sei.cmu.edu; frame-src www.youtube.com; report-uri https://algosecure.report-uri.com/r/d/csp/enforce"

Après avoir vérifié que la politique fonctionne correctement et ne bloque pas la navigation sur notre site, nous changerons l'instruction Content-Security-Policy-Report-Only en Content-Security-Policy. Tant que nous laissons le mode Report-Only, rien ne sera bloqué, la navigation ne sera pas impactée côté client.

La dernière étape consiste à d'implémenter cette politique dans le composant du serveur qui sert les pages de votre site à vos utilisateurs.

Implémenter la Content-Security-Policy

Apache

Nous plaçons l'instruction suivante dans le fichier global security.conf (ou dans les paramètres du vhost si l'on possède plusieurs sites hébergés sur le même serveur) :

Header always set Content-Security-Policy-Report-Only "default-src 'self' nostats.algosecure.fr [...]"

Il faut ensuite activer le module headers, puis redémarrer Apache :

sudo a2enmod headers
sudo service apache2 restart

Nginx

L'instruction suivante est à placer dans le contexte serverdu fichier de configuration :

add_header Content-Security-Policy-Report-Only "default-src 'self' nostats.algosecure.fr [...]";

Puis on recharge la configuration :

nginx -s reload

IIS

Par interface graphique, il faut aller dans les paramètres des "En-têtes de réponse HTTP", puis ajouter un entête personnalisé :

Il est également possible d'injecter la section de code suivante dans le contexte customHeaders du contexte httpProtocol de votre fichier web.config :

<add name="Content-Security-Policy-Report-Only" value="default-src 'self' nostats.algosecure.fr [...]" />

Les modifications prennent effet immédiatement.

Tester sa Content-Security-Policy

Une fois la politique implémentée, vous pouvez vous rendre sur le site cspvalidator.org qui vous indiquera si la politique en place est valide.

Surveiller sa Content-Security-Policy

À ce stade, la politique est implémentée, mais de par l'utilisation du mode Report-Only, rien n'est bloqué côté client. Il faut surveiller que la politique ne crée pas d'interférences avec l'affichage du site, la retravailler pour ajouter ou supprimer des sources, dans l'objectif de passer du mode Report-Only au mode "normal".

Pour cela, le site Report-URI fonctionne bien : il permet d'afficher toutes les ressources qui seraient bloquées côté client. Il convient d'analyser ces blocages, déterminer ceux qui sont légitimes ou non, puis faire évoluer votre Content-Security-Policy en conséquence.

Voici par exemple un rapport d'une ressource ayant été bloquée chez des clients. Ce rapport est en l'occurrence assez simple à comprendre : nous avions inclus une instruction de style au sein du code HTML (donc inline) d'une des pages du site, alors que nous n'avions pas explicitement autorisé la source de contenu inline. Pour résoudre le problème, il nous a suffit de déplacer cette instruction dans un fichier de style CSS (comme ça aurait dû l'être dès le début).

Il faut ainsi passer sur chaque blocage, et évaluer s'il est pertinent ou non d'autoriser le chargement de la ressource sur votre site.

Une fois que vous vous serez assuré que tout fonctionne correctement, vous pourrez supprimer la partie -Report-Only du nom de l'entête configuré dans votre serveur web, puis recharger la configuration. Félicitations, votre Content-Security-Policy est en place et fonctionnelle !

Conclusion

Content-Security-Policy est pour nous l'un des entêtes HTTP les plus puissants en termes de sécurité. Il vous permet de garder la main sur les ressources externes chargées sur votre site. Bien que cela ne constitue pas une protection suffisante pour se prémunir de tous types d'attaques, cet entête offre un niveau de sécurité assez élevé aux sites qui l'implémentent. Son taux d'adoption mérite d'être amélioré. Pour se prémunir encore davantage à l'injection de contenu malveillant sur un site, il est possible d'utiliser Sub-Resource Integrity : ce sera peut-être le sujet d'un prochain article.

Des doutes quant à l'implémentation de cet entête ? N'hésitez pas à nous contacter, nous nous ferons un plaisir de répondre à vos interrogations :)

La vache qui root

Jonathan 25 octobre 2016 Public technique

Une faille corrigée peut réapparaître quelques années plus tard. C'est le cas pour une faille hautement critique découverte par Phil Oester dans le kernel Linux. La faille vieille de 9 ans est une élévation de privilège, c'est à dire qu'un utilisateur avec des droits restreints peut exécuter une action avec des droits administrateurs (root dans le cas présent).

Dirty COW

La vulnérabilité est surnommée Dirty COW en référence au mécanisme Copy-On-Write du noyau Linux. Elle correspond à la CVE-2016-5195. Comme écrit plus haut, il s'agit d'une faille permettant à un simple utilisateur d'exécuter des programmes avec les privilèges root. Cette faille est d'autant plus critique que :

  • Il est facile d'écrire un exploit, une liste de POC est disponible ici
  • La faille est présente dans quasiment toutes les différentes distributions Linux, on peut citer Debian, Ubuntu et certaines version de RedHat...

La faille a déjà été corrigée pour les distribution RedHat, Debian et Ubuntu, pour les autres il faudra attendre avec patience...

Fonctionnement

Dans le noyau Linux, lorsqu'un processus souhaite écrire dans un fichier, une copie de ce dernier est créée au moment de l'écriture : c'est le mécanisme de Copy-On-Write. La vulnérabilité est basée sur cette fonctionnalité. L'exploitation est décomposée en plusieurs étapes :

  • Récupérer l'adresse mémoire de la copie du fichier avec la fonction mmap(). Le contenu du fichier est "mappé" en mémoire.
  • Effectuer parallèlement deux actions à l'aide de 2 threads:
    1. La première action est une écriture en boucle dans la zone mémoire pour générer des copies (Cow) de la zone.
    2. La deuxième action est l'utilisation de la fonction madvice() avec l'argument MADV_DONTNEED. Ce dernier indique au système qu'il peut libérer la zone mémoire utilisée.

La combinaison de ces deux actions peut provoquer une race condition, c'est à dire que l'écriture est effectuée avant que la copie en mémoire soit créée.

La vidéo suivante permet de donner plus de détails techniques sur l'exploit et la faille en elle-même:

Démonstration

Je me connecte en tant que root, pour créer un fichier accessible uniquement en lecture

(jonathan)$ sudo -s
(root)$ echo "Je suis root, personne ne peut modifier ce fichier" > test_DirtyCow
(root)$ exit
(jonathan)$ ls -ltr test_DirtyCow
-rw-r--r-- 1 root root 51 oct.  26 11:02 test_DirtyCow
(jonathan)$ echo "Algosecure is the best!" > test_DirtyCow
-bash: test_DirtyCow: Permission non accordée

Je ne peux donc pas modifier le fichier avec mes droits actuels. Je vais maintenant utiliser le POC dirtyCow pour modifier le fichier

(jonathan)$ ./dirtyc0w test_DirtyCow "Algosecure is the best et peut réécrire le fichier            "
mmap 7f6921ee3000

madvise 0

procselfmem 2005032704
(jonathan)$ cat test_DirtyCow 
Algosecure is the best et peut réécrire le fichier
(jonathan)$ echo "Algosecure is the best!" > test_DirtyCow
-bash: test_DirtyCow: Permission non accordée

Le fichier a bien été modifié. Cependant si le contenu à écrire dans le fichier dépasse la taille réelle du fichier, l'écriture sera tronquée. Inversement, si le contenu est plus petit, alors seul les n premiers octets du fichier seront écrasés.

Ce test n'est pas significatif de la criticité de la vulnérabilité. Cependant, en modifiant un fichier comme /etc/shadow ou les exécutables /bin/ls, /bin/cat, /bin/ping les conséquences seraient beaucoup plus importantes.

Pour aller plus loin...

Cette faille ne touche pas uniquement les machines Linux, elle concerne également tous les terminaux Android. Ce lien explique comment procéder pour la version Android.