Déployer CSP : une approche en 5 étapes

Peut-être avez-vous vu ce conseil chez Dareboost vous encourageant à mettre en place une politique de sécurité sur la provenance de vos ressources. Mais peut-être n’avez-vous pas encore franchi le cap. Pour vous aider à sauter le pas, je vous propose quelques conseils sur son déploiement, tirés de mon expérience.

CSP, pour Content Security Policy, est un en-tête de sécurité destiné à lutter contre les failles dites de Cross-Site Scripting (XSS). Le principe, déjà expliqué ici, en est simple : vous définissez des directives de sécurité à propos des contenus de vos pages et le navigateur se charge de les appliquer. En pratique, cet en-tête est très puissant et permet énormément de choses.

Son implémentation peut néanmoins effrayer, CSP étant un sujet assez complexe. Intéressé par le sujet depuis des années et pratiquant convaincu, je voulais partager avec vous une méthode d’implémentation tirée de mon expérience. Mais d’abord, un peu de contexte.

CSP n’est pas binaire

En matière de sécurité, il est de coutume de croire qu’on a affaire à un interrupteur. Un site web est soit sécurisé, soit il ne l’est pas.

Cette vision binaire de la sécurité est fausse : la sécurité est un processus et, en tant que tel, peut avoir différentes gradations dictées par votre contexte, votre historique, vos besoins, votre budget, etc. CSP ne fait pas exception à la règle. Vous entendrez d’ailleurs souvent l’expression « réduire la voilure » sur le Front-End grâce à CSP : à l’instar du marin adaptant sa prise au vent en fonction des conditions climatiques plus ou moins délicates, votre implémentation de CSP sera plus ou moins restrictive selon vos besoins en sécurité. Un site extrêmement sensible ne sera pas soumis aux mêmes exigences qu’un site vitrine.

Autre point important à évaluer quand on se pose la question de déployer CSP : quelle est votre maîtrise de votre Front-End ?

Quand on parle de maîtrise, il n’est pas question ici de prouver que vous êtes doué en CSS ou un dieu du JavaScript… Mais bien de maîtriser ce qui se passe côté client.

En d’autres termes, avez-vous une vision exhaustive de vos sources de contenus (scripts, CSS, webfonts, services tiers, etc.) ?

Connaissez-vous exactement l’impact de chaque script, ressource ou CSS ? Leurs rôles dans le rendu ? Ce dont tout cela a besoin pour fonctionner correctement ? Ce que cela fait exactement ?

Savez-vous quelles portions de code sont voulues et maîtrisées par vos équipes Front-End et quelles portions sont inhérentes aux technologies Back-End employées — et donc difficilement modifiables ?

Notez qu’il n’y a pas de jugement de valeur dans ces questions : dans certains contextes, je vais pouvoir dire que j’ai cette connaissance (amenée par l’expertise, les outils, la documentation, etc.), et dans d’autres que je ne l’ai pas du tout.

Cette connaissance fine est souhaitable mais parfois délicate ou impossible à obtenir.

Différents niveaux

Si vous avez pu répondre par l’affirmative à toutes ces questions ci-dessus, alors vous saurez sûrement ce que vous pourrez déployer comme directives. Démarrez votre déploiement avec Content-Security-Policy-Report-Only pour éviter que les directives bloquent des éléments à tort et à travers. Et utilisez report-uri pour récupérer les infractions. En toute logique, cela devrait assez bien se passer. Tout au plus aurez-vous quelques menus réglages à effectuer, et vous pourrez déployer. Vous aurez fait le plus dur en faisant l’inventaire de ce qui se passe sur votre Front-End.

Dans le cas contraire, il va falloir combler cette absence de connaissances, et déployer CSP petit à petit , en réduisant la voilure progressivement.

Lors d’un atelier à l’édition 2017 de Paris Web (dont Dareboost était sponsor), j’ai proposé 5 « niveaux » d’implémentation de CSP. Encore une fois, je le redis : ce ne sont pas des notations de votre travail !

Dans certains cas, je reste moi-même au niveau 1 car je n’ai pas la possibilité d’aller plus loin.

Voyez-le plutôt comme le niveau de voilure que vous souhaitez réduire (les restrictions des niveaux supérieurs incluent celles des niveaux précédents). Autre point important, si vous avez la possibilité d’emprunter des éléments à divers niveaux sans forcément les atteindre pleinement, ne vous en privez pas !

Rappel : lors de l’implémentation, prenez soin de rester en Content-Security-Report-Only et de préciser, en plus des directives que je vais indiquer, une valeur pour la directive report-uri. Dans le dépôt CSP-useful, je liste plusieurs méthodes (envoi d’un mail, d’un objet JSON, stockage en base…) pour récupérer les infractions constatées par le navigateur et adapter votre stratégie de contrôle.

Implémentation « une étoile »

Badge CSP ★

Content-Security-Policy:
default-src * data: ;
script-src * 'unsafe-inline' 'unsafe-eval'  ;
style-src * 'unsafe-inline' data: ;
frame-ancestors 'none' ; # none ou ce que vous autorisez

Comme il faut bien démarrer avec quelque chose, ce premier niveau autorise tout ce qui se passe sur le site (scripts/CSS en ligne, fonction eval, data-uri, toutes origines, etc.). Il restreint juste la possibilité d’embarquer le site dans des frames. C’est un équivalent de l’en-tête X-Frame-Options (à ceci près que vous pouvez définir précisément les sources pour frame-ancestors). Cela devrait être votre minimum vital.

Implémentation « deux étoiles »

Badge CSP ★★

Autant le dire tout de suite : le niveau précédent ne vous protègera de rien d’autre que des attaques de types clickjacking. Il est intéressant maintenant de préciser vos sources pour vos différents éléments afin de ne plus tout autoriser.

Content-Security-Policy:
default-src 'self' data: ;
script-src 'self' www.foo.com 'unsafe-inline' 'unsafe-eval' ;
style-src 'self' www.coincoin.com 'unsafe-inline' data: ;
frame-ancestors 'none' ;
base-uri 'none' ; # none ou ce que vous autorisez

Ici, vous précisez vos sources (ce qui permet d’éviter le sélecteur wildcard « * ») et définissez base-uri (soit à none, soit à ce dont vous avez besoin selon votre utilisation de l’élément base : self, etc.).

Implémentation « trois étoiles »

Badge CSP ★★★

Le niveau précédent restreint les origines, mais encore beaucoup de choses sont ouvertes. Et si nous regardions les web fonts et CSS ?

Content-Security-Policy:
default-src 'self' data: ;
script-src 'self' www.foo.com 'unsafe-inline' 'unsafe-eval' ;
style-src 'self' www.coincoin.com data: ;
font-src 'self' ;
frame-ancestors 'none' ;
base-uri 'none' ;

La différence par rapport au niveau précédent est qu’ici, on s’interdit unsafe-inline pour les styles (donc plus de styles inline non protégés par hash/nonce, et plus d’attribut style sur des éléments HTML), et on s’enlève également la dangereuse possibilité des data-uri pour le JS (fortement déconseillée, car un bon vecteur d’attaques XSS).

Il s’agit probablement du premier niveau qui peut être difficile à atteindre puisqu’il impose de repenser la façon dont certains styles sont posés dans la page. Typiquement, les scripts qui ont une fâcheuse tendance à poser des styles inline vont devoir être soit nettoyés, soit remplacés. Si cela peut sembler être une contrainte à première vue, vos sites vont aussi bénéficier de ne plus se voir imposer ces styles parfois handicapants.

Si vous pensez que les styles ne peuvent qu’être inoffensifs, je vous invite à lire ceci : CSS Exfil Vulnerability [EN].

Quant aux webfonts, regardez comment il est possible d’utiliser CSS et des webfonts pour fabriquer un keylogger.

Implémentation « quatre étoiles »

Badge CSP ★★★★

Un premier palier – et pas le moindre – a été franchi en maîtrisant CSS. Mais là n’est pas le principal vecteur d’attaques XSS , vous vous en doutez bien. Il est temps d’entrer dans l’arène en s’attaquant à JavaScript !

Content-Security-Policy:
default-src 'self' ;
script-src 'self' www.foo.com  sha256-abcdef ;
style-src 'self' www.coincoin.com data: ;
font-src 'self' ;
frame-ancestors 'none' ;
base-uri 'none' ;

Ici, on ajoute des restrictions fortes sur les scripts JavaScript : plus d’unsafe-inline (donc plus de JS inline non-protégé par hash/nonce dans vos pages). En clair, soit votre JS inline est protégé par un hash/nonce, soit vous le stockez dans des fichiers externes servis sur des origines approuvées.

Et voulez-vous bien restreindre cette fonction eval bien trop dangereuse ? :) Interdisez-vous unsafe-eval, pour couper court à toute une gamme de vecteurs d’attaques. Vous ne vous en porterez pas plus mal.

Il s’agit ici du second gros palier, qui peut être très difficile à atteindre. Si votre Back-End génère des scripts (typiquement SharePoint), il faudra soit qu’il s’en passe, soit qu’il génère aussi les nonces/hashes qui permettront de les autoriser sans pour autant autoriser les scripts inline. Sinon, vous devrez aussi « recenser » les scripts inline devant être autorisés (par nonce ou hash).

Ce palier va permettre à vos équipes Back et Front de travailler de concert à la maîtrise complète de votre site.

Implémentation « cinq étoiles »

Après avoir passé deux paliers assez costauds pour maîtriser CSS et JS, ce « dernier » niveau a de grandes chances… de vous paraître assez facile à atteindre en comparaison.
Il consiste à ne rien s’autoriser par défaut.

Content-Security-Policy:
default-src 'none' ;
script-src 'self' www.foo.com  ;
style-src 'self' www.coincoin.com data: ;
font-src 'self' ;
frame-ancestors 'none' ;
base-uri 'none' ;

Vous pouvez détailler davantage pour affiner vos besoins mais avec default-src à 'none', tout sera bloqué par défaut pour ces directives :

  • connect-src
  • font-src
  • frame-src
  • img-src
  • media-src
  • object-src
  • script-src
  • style-src
  • worker-src (CSP 3)
  • manifest-src (CSP 3)

Le déblocage des ressources directive par directive vous permettra de parfaitement comprendre ce qui se passe sur votre Front-End pour pouvoir réagir et adapter.

Si vous n’avez pas de besoin de plug-ins, vérifiez bien que object-src est à none. N’oubliez pas non plus la directive form-action pour vos formulaires.

Badge CSP ★★★★★

Toujours plus loin, toujours plus haut ?

Ne voyez pas cette étape comme un Saint-Graal, même si vous serez content d’arriver là, mais il est encore possible de régler plus finement vos directives.

CSP niveau 2 permet de restreindre les origines avec des chemins (ex. : vous pouvez n’autoriser que foo.com/scripts, seuls les fichiers dans ce chemin seront autorisés).

Si vous le pouvez, utilisez les contrôles d’intégrité (hashes) plutôt que les origines. Ils vous protègeront contre les modifications intempestives et irréfléchies pouvant provenir de vos propres équipes.

Des choses plus strictes se préparent avec CSP niveau 3 : il va être possible par exemple de n’autoriser que les scripts et/ou les styles utilisés qui sont protégés par SubResource Integrity (SRI), ou de vérifier aussi les fichiers externes via hashes (pour l’instant, seuls les scripts/styles inline peuvent en bénéficier), voire de combiner ces deux possibilités en exigeant d’un script qu’il soit protégé par SRI et que le hash appartienne à une liste explicite de valeurs.

Et plus prosaïquement : si vous en êtes à ces niveaux-là et que cela ne suffit pas, votre problème n’est plus CSP… mais très probablement d’autres maillons de la chaîne de sécurité. Et peut-être même d’embaucher des personnes très compétentes et très spécialisées, qui seront dédiées à gérer vos besoins en ce domaine ! :)

Conclusion

CSP a ceci d’assez unique : c’est une technologie de sécurité qui implique les équipes Back-End comme les équipes Front-End (et même très fortement ces dernières).

Soyez toujours pragmatique avec CSP (et pas qu’avec ce dernier d’ailleurs) : visez des directives en rapport avec vos besoins et vos contextes. Si vous avez l’occasion de prévoir cela en amont, c’est toujours plus facile. N’hésitez pas à impliquer et expliquer le fonctionnement et l’intérêt aux équipes Marketing qui sont très concernées par les limitations et ne comprennent pas toujours les objectifs recherchés. Une refonte est un excellent moment pour nettoyer votre code Front-End, imposer des bonnes pratiques ainsi qu’ un peu de sobriété.

N’hésitez également pas à mettre en place une intégration continue capable de remonter les infractions relevées par votre CSP. Cela peut aller d’un système d’alerte interne capable de prévenir en cas d’infractions répétées de vos directives à un système capable de parcourir une version du site candidate pour la production afin d’y vérifier qu’aucune dépendance critique n’est bloquée.

Vous pouvez également faire petit à petit des composants qui sont CSP-friendly, comprenez par là qu’ils nécessitent peu de directives pour marcher. C’est un travail parfois de longue haleine, mais le jeu en vaut la chandelle.

Si vous utilisez Dareboost, vous avez déjà sûrement constaté que de nombreux services tiers ne sont pas aussi soucieux de la qualité que vous pouvez l’être. CSP est un motif de plus d’être exigeant avec ces derniers… ou d’être plus parcimonieux dans leur utilisation !

Je remercie vivement Boris Schapira et Philippe Guilbert pour tout le travail d’affinage et de formulation.

Ressources additionnelles

A propos Nicolas Hoffmann

Développeur Front-end amoureux de CSS, de CSP et de sécurité, de web mobile, d'accessibilité, de WebPerf et de Qualité Web. Papa de Röcssti/@Van11y & de plugins accessibles. Membre de l'équipe @parisweb et d'Openweb.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

*