- par
- Watilin
- le
- 26/02/2017 à 15:21
- catégorie
- Informatique
CSP ou De l’importance de passer des fonctions à setTimeout
Un article écrit rapidement ce soir, pour présenter une nouvelle mécanique de sécurité des applications web, et pour ajouter à la liste des raisons pour lesquelles passer des chaînes à setTimeout
et setInterval
est généralement considéré comme une mauvaise pratique. Note : je parlerai seulement de setTimeout
par la suite, mais les deux sont interchangeables.
Content Security Policy
Avez-vous entendu parler de Content Security Policy (CSP) ? C’est un mécanisme de protection assez récent, un nouvel outil donné aux développeurs web pour se prémunir contre les attaques impliquant un domaine tiers (attaques XSS).
En quelques mots, CSP est un ensemble de nouveaux en-têtes HTTP définissant le type de ressources avec lesquelles une page a le droit d’interagir, en fonction de leur origine. Prenons un exemple simple, une page envoyée avec cet en-tête :
Content-Security-Policy: default-src 'self'
Si cette page est consultée avec un navigateur supportant CSP, ce dernier n’acceptera de charger une ressource, quel que soit son type (script, feuille de style, image, iframe, etc.) que si cette ressource provient de la même origine que la page, désignée par le mot-clé 'self'
.
Origine a un sens particulier ici, il s’agit de la combinaison du nom d’hôte et du numéro de port (quand il est spécifié). Par exemple, sous ce point de vue, kergoz-panic.fr:443
et kergoz-panic.fr:12345
seront considérées comme des origines différentes.
Notez que CSP est future-proof : un type de ressource qui n’existe pas encore est d’ores-et-déjà bloqué par le mécanisme.
L’option unsafe-inline
CSP fonctionne sur le principe d’une liste blanche : par défaut, il bloque tout ; il faut nommer explicitement ce qui est autorisé. Ce dont vous ne vous doutez peut-être pas, c’est qu’il bloque par défaut le contenu inline, ce qui signifie :
- les balises
<script>
contenant directement du code ; - les attributs d’évènements tels que
onclick
,onmouseover
, etc. ; - les balises
<style>
; - les attributs
style
.
Oui, les styles aussi sont concernés, car il a été prouvé plus d’une fois que le CSS peut être un vecteur d’attaque.
Le blocage du contenu inline ne devrait pas vous importuner si vous séparez proprement les couches de vos applications : la couche contenu (HTML), la couche présentation (CSS), et la couche comportement (JavaScript). Au minimum un fichier par couche, et ainsi on ne mélange pas les codes.
Dans le cas, encore très fréquent aujourd’hui, où vos couches ne sont pas bien séparées pour une raison ou une autre, vous êtes alors obligés d’autoriser le contenu inline, en ajoutant l’option unsafe-inline
à votre directive CSP.
Content-Security-Policy: default-src 'self' 'unsafe-inline'
Pour entrer dans les détails, il y a d’autres directives que default-src
, qui permettent de contrôler plus finement les permissions des différents types de ressources. Voir cette doc – c’est en anglais mais je prendrai le temps de la traduire un de ces quatre.
L’option unsafe-eval
Mais ce n’est pas tout ! CSP bloque aussi, par défaut, la tristement célèbre fonction eval
, ainsi que ses dérivés. Et parmi les dérivés d’eval
il y a quoi ? Il y a new Function
, et il y a setTimeout
quand on lui passe une chaîne. Je reviens sur ce point dans un instant – après tout, c’est le sujet de cet article.
Dans le cas où, pour une raison ou une autre encore une fois, vous auriez absolument besoin de recourir à eval
ou de passer une chaîne à setTimeout
, vous devez l’expliciter à CSP avec l’option unsafe-eval
.
Content-Security-Policy: default-src 'self' 'unsafe-eval'
Remarquez comme les créateurs du mécanisme ont choisi les noms qui font réfléchir… :P
Resident eval
(même pas désolé)
Quand vous passez une chaîne à setTimeout
, le moteur JavaScript doit faire appel à l’analyseur de code pour évaluer cette chaîne, exactement comme pour eval
, et avec les mêmes inconvénients :
- La chaîne est évaluée au moment de l’appel (runtime), et non pas à l’interprétation initiale du script. Difficile pour l’analyseur de savoir à l’avance ce que contient la chaîne, quelles variables elle va déclarer ou modifier, de quelle quantité de mémoire elle aura besoin, etc. La plupart des procédés d’optimisation ne sont plus applicables.
- Le code sera évalué dans le contexte global, il n’a donc pas accès à l’environnement depuis lequel
setTimeout
a été appelé. On perd donc l’accès aux variables locales et celles des scopes supérieurs. Ce phénomène peut conduire à d’autres mauvaises pratiques, telles qu’un usage excessif de variables globales. - Bien entendu, il faut mentionner le risque de sécurité si on passe une chaîne saisie par l’utilisateur. Cela dit, ce cas de figure est moins susceptible de se produire avec
setTimeout
car, en général, les développeurs lui passent une chaîne statique, là où leur intention réelle est de lui passer une fonction sans savoir que c’est possible.
Briser ses chaînes
Je pense que tous les développeurs JavaScript qui lisent cet article savent comment on écrit une fonction. Juste pour être parfaitement clair, je vous mets un exemple de code où setTimeout
est appelée avec une fonction.
let timer = setTimeout(function () {
console.log('Bonjour :)');
}, 1000);
Et en bonus, la version « fonction flèche » (arrow) de la norme ES2015 :
let timer = setTimeout(() => {
console.log('Bonjour :)');
}, 1000);
C’est tout
Je vous parle de tout ça parce que j’ai été confronté aujourd’hui à une situation dans laquelle l’usage d’eval
pouvait être jugé légitime, mais où le développeur se souciait malgré ça de la sécurité de son application.
Si vous êtes dans la même situation que cette personne, CSP pourrait bien vous aider. En tout cas, c’est une technologie qui mérite qu’on s’y attarde. Il me semble qu’elle est plutôt bien construite : elle est cohérente, on en comprend rapidement le fonctionnement, elle est facile à mettre en œuvre et elle encourage les bonnes pratiques comme la séparation des couches et le passage de fonctions à setTimeout
. Et cerise sur le gâteau, son support est déjà largement répandu !
Alors, vous avez compris les enfants ? Ne passez plus de chaînes à setTimeout
;)
Commentaires
Aucun commentaire n’a encore été posté sur cet article.