- Pour prévenir les attaques XSS, l’une des principales vulnérabilités du web, Firefox est le premier à prendre en charge l’API Sanitizer standardisée
- En utilisant la méthode
setHTML() à la place de innerHTML, le HTML non fiable est automatiquement assaini (sanitize) avant son insertion dans le DOM, ce qui supprime les scripts malveillants
- Les développeurs peuvent contrôler les éléments et attributs autorisés via une configuration personnalisée si les réglages par défaut sont trop stricts ou pas assez
- Associée aux Trusted Types, cette fonctionnalité de Firefox élève le niveau de sécurité du web dans son ensemble et permet de prévenir les XSS même sans équipe sécurité dédiée
Vulnérabilités XSS et réponse de Firefox
- Le cross-site scripting (XSS) se produit lorsqu’un attaquant injecte du HTML ou du JavaScript arbitraire via un contenu saisi par l’utilisateur
- Il peut alors surveiller les interactions des utilisateurs ou voler des données
- Les XSS sont classées parmi les trois principales vulnérabilités du web (CWE-79) depuis près de dix ans
- Firefox renforce la défense contre les XSS depuis 2009 en jouant un rôle moteur dans le standard Content-Security-Policy (CSP)
- Le CSP limite les ressources qu’un site web peut charger et exécuter
- Mais son adoption à grande échelle restait limitée, car il exigeait des changements dans la structure des sites existants et des revues de sécurité continues
Rôle de l’API Sanitizer et de setHTML()
- L’API Sanitizer fournit une méthode standardisée pour transformer du HTML malveillant en contenu inoffensif
- Dans l’exemple de code, l’élément
<img src="x" onclick="alert('XSS')"> est supprimé et seul <h1>Hello my name is</h1> reste
- La méthode
setHTML() applique automatiquement le processus d’assainissement lors de l’insertion de HTML, garantissant un comportement sûr par défaut
- Il suffit de remplacer une affectation
innerHTML existante par setHTML() pour obtenir une protection XSS robuste
- Si la configuration par défaut est trop stricte ou trop permissive, les développeurs peuvent définir les éléments et attributs HTML autorisés via une configuration personnalisée
- L’outil Sanitizer API playground peut être utilisé pour faire des essais
Association avec les Trusted Types
- L’API Trusted Types offre une couche de sécurité supplémentaire en centralisant le contrôle du parsing et de l’insertion du HTML
- Lors de l’utilisation de
setHTML(), il est facile d’appliquer une politique Trusted Types
- Une politique stricte peut n’autoriser que
setHTML() et bloquer les autres méthodes d’insertion risquées, contribuant ainsi à prévenir de futures régressions XSS
Effets des améliorations de sécurité dans Firefox 148
- Firefox 148 prend en charge à la fois l’API Sanitizer et les Trusted Types, ce qui améliore fortement le niveau de sécurité par défaut
- Les développeurs peuvent prévenir les XSS avec de simples changements de code, sans politiques de sécurité complexes ni équipe sécurité dédiée
- L’introduction de ce standard devrait contribuer à étendre un environnement web plus sûr à l’ensemble des navigateurs
Résumé
- Firefox 148 aide les développeurs web à bloquer facilement les attaques XSS grâce à la méthode
setHTML() et à l’API Sanitizer
- Cette fonctionnalité comble les limites du CSP et marque une étape vers un standard web d’insertion HTML sûre par défaut
- Associée aux Trusted Types, elle permet un maintien durable de la sécurité et la prévention des régressions XSS
- En conséquence, Firefox conduit la transition vers un environnement web où la sécurité est le choix par défaut
2 commentaires
Oh oui, c’est clairement le genre de chose dont on a besoin. Ce serait vraiment génial si tous les navigateurs le prenaient en charge.
Réactions sur Hacker News
Ce genre de fonctionnalité met toujours un peu mal à l’aise
car il existe un mélange de méthodes qui traitent en toute sécurité une entrée utilisateur arbitraire et d’autres qui ne le font pas, et rien qu’au nom il est difficile de les distinguer
Idéalement, les fonctions dangereuses devraient l’indiquer clairement dès leur nom
En plus, la notion même de « sanitization » du HTML est floue, et il est difficile de juger si c’est réellement sûr
Les éléments et attributs capables d’exécuter des scripts sont supprimés, et cette logique fonctionne à l’intérieur du moteur du navigateur, ce qui permet un traitement plus précis qu’un sanitizer basé sur des chaînes de caractères
Voir la documentation MDN de setHTML pour plus de détails
elementNode.textContentest sûr même avec des entrées non fiables, alors queelementNode.innerHTMLne l’est pasLe premier échappe tous les caractères, le second n’échappe rien du tout
Certains estiment que la « sanitization HTML » est un problème fondamentalement insoluble
Voir ce commentaire pour la discussion associée
Une API de ce type n’aurait pas dû passer l’étape de proposition
innerHTMLetsetHTML, il aurait fallu supprimer complètementinnerHTMLet utilisersetHTMLUnsafelorsqu’on a besoin de l’ancien comportementinnerHTMLCela dit, dans ce cas le site pourrait ne plus fonctionner sur les anciens navigateurs
Content-Security-Policy: require-trusted-types-for 'script', on peut bloquer le passage de chaînes ordinaires à des méthodes sans sanitizerSi, comme dans l’exemple, on peut insérer des balises comme
<h1>ou<br>dans un nom d’utilisateur, alors même si l’exécution de scripts est bloquée, on peut toujours faire de l’injection de balisage arbitraireOn peut même modifier le CSS avec une balise
<style>, et par exemple altérer le design d’une page de profil PayPalOn peut se demander qui voudrait de ça
On peut ajouter une couche de défense en prenant le HTML généré par Markdown puis en le restreignant à nouveau avec un sanitizer qui n’autorise que certaines balises
innerHTMLnon, maisinnerTextoutextContentsetHTMLest pensé comme un remplaçant deinnerHTMLsetHTML()est trop stricte ou trop permissive, le développeur peut fournir une configuration personnalisée définissant explicitement les éléments HTML et attributs autorisésVoir ce fil pour la discussion associée
Au final, côté backend il faut toujours sanitizer les noms d’utilisateur de manière standard, et à l’affichage il faut appliquer un échappement HTML
Selon la RFC 2119, c’est une exigence de niveau « SHOULD »
Je suis content de voir arriver cette fonctionnalité, mais il faudra sans doute du temps avant que le support navigateur soit suffisamment répandu
On peut vérifier l’état de la prise en charge sur Can I use
En attendant, on peut utiliser un polyfill
Le titre était un peu sensationnaliste
En pratique, on peut aussi implémenter une sanitization avec une fonction qui valide l’entrée avant de la passer à
innerHTMLCela dit, ce genre de tentative donne finalement l’impression de réinventer la roue
Et sur les anciennes versions de Firefox, hacks.mozilla.org ne s’ouvre même pas, tandis que sur Pale Moon ou SeaMonkey, MDN s’affiche mal
On a l’impression qu’un « cartel des navigateurs » essaie de détruire le web
C’est aussi un argument qui ne tient pas compte des problèmes de parser differential
La Sanitizer API peut devenir un footgun si elle est mal utilisée
Il faut surtout faire attention lorsqu’on utilise le mode « remove »
Personnellement, je pense qu’il vaudrait mieux s’en tenir à
setTextet ne permettre aucun ajout de HTML par l’utilisateursetHTML, il n’y aura pas de XSSVu la fréquence d’usage de
innerHTML, il est difficile de l’exclure totalementIl est impressionnant de voir que tous les aspects de l’accès réseau sont désormais correctement contrôlés, et que la chaîne de sécurité est passée de la confiance dans le code à la confiance dans la configuration de l’hôte
Les valeurs par défaut sont aussi sécurisées
Ce que je veux vraiment, c’est un élément
<sandbox>capable d’exécuter du code dangereux en toute sécuritéL’idée n’est pas de modifier le code dangereux, mais de pouvoir l’exécuter dans un environnement isolé
Les iframe ont la contrainte de ne pas pouvoir s’intégrer naturellement au flux du DOM, et à une époque d’IA et de contenu dynamique croissant, il faut une encapsulation composable
J’aime vraiment le nom
setHTMLUnsafeLes fonctions de sécurité échouent quand elles reposent sur un opt-in du développeur
Il est plus efficace de faire en sorte que « le chemin dangereux paraisse dangereux »
Le nom
set_html()est bien plus intuitif queinner_htmlLes API JavaScript sont vraiment incohérentes et mériteraient un jour d’être remises en ordre
Ici la discussion porte sur la sécurité, mais lorsqu’on publie une nouvelle API, sa conception devrait aussi être propre
Les API DOM donnent depuis longtemps, et encore aujourd’hui, l’impression d’avoir été conçues par des gens qui n’ont jamais créé d’API
Les développeurs des années 90 :
Les développeurs des années 2020 :
Nous n’avons rien appris des années 90