DOMPurify est un désinfectant XSS uniquement DOM, ultra-rapide et ultra-tolérant pour HTML, MathML et SVG.
Il est également très simple à utiliser et à démarrer. DOMPurify a été lancé en février 2014 et a entre-temps atteint la version v3.2.1 .
DOMPurify est écrit en JavaScript et fonctionne dans tous les navigateurs modernes (Safari (10+), Opera (15+), Edge, Firefox et Chrome - ainsi que presque tout ce qui utilise Blink, Gecko ou WebKit). Il ne tombe pas en panne sur MSIE ou d'autres navigateurs existants. Cela ne fait tout simplement rien.
Notez que DOMPurify v2.5.7 est la dernière version prenant en charge MSIE. Pour les mises à jour de sécurité importantes compatibles avec MSIE, veuillez utiliser la branche 2.x.
Nos tests automatisés couvrent actuellement 24 navigateurs différents, et d'autres à venir. Nous couvrons également Node.js v16.x, v17.x, v18.x et v19.x, exécutant DOMPurify sur jsdom. Les anciennes versions de Node sont également connues pour fonctionner, mais bon... aucune garantie.
DOMPurify est écrit par des responsables de la sécurité qui possèdent une vaste expérience en matière d'attaques Web et de XSS. N'ayez crainte. Pour plus de détails, veuillez également lire nos objectifs de sécurité et notre modèle de menace. S'il vous plaît, lisez-le. Genre, vraiment.
DOMPurify nettoie le HTML et empêche les attaques XSS. Vous pouvez alimenter DOMPurify avec une chaîne pleine de HTML sale et il renverra une chaîne (sauf configuration contraire) avec du HTML propre. DOMPurify supprimera tout ce qui contient du HTML dangereux et empêchera ainsi les attaques XSS et autres méchancetés. C'est aussi sacrément rapide. Nous utilisons les technologies fournies par le navigateur et les transformons en filtre XSS. Plus votre navigateur est rapide, plus DOMPurify sera rapide.
C'est facile. Incluez simplement DOMPurify sur votre site Web.
<script type="text/javascript" src="src/purify.js"></script>
<script type="text/javascript" src="dist/purify.min.js"></script>
Ensuite, vous pouvez nettoyer les chaînes en exécutant le code suivant :
const clean = DOMPurify.sanitize(sale);
Ou peut-être ceci, si vous aimez travailler avec Angular ou similaire :
importer DOMPurify depuis 'dompurify';const clean = DOMPurify.sanitize('<b>bonjour</b>');
Le HTML résultant peut être écrit dans un élément DOM en utilisant innerHTML
ou dans le DOM en utilisant document.write()
. Cela dépend entièrement de vous. Notez que par défaut, nous autorisons HTML, SVG et MathML. Si vous n'avez besoin que de HTML, ce qui peut être un cas d'utilisation très courant, vous pouvez également le configurer facilement :
const clean = DOMPurify.sanitize(dirty, { USE_PROFILES: { html: true } });
Eh bien, veuillez noter que si vous nettoyez d'abord le HTML, puis que vous le modifiez ensuite , vous pourriez facilement annuler les effets de la désinfection . Si vous transmettez le balisage nettoyé à une autre bibliothèque après la désinfection, assurez-vous que la bibliothèque ne manipule pas le code HTML d'elle-même.
Après avoir nettoyé votre balisage, vous pouvez également consulter la propriété DOMPurify.removed
et découvrir quels éléments et attributs ont été supprimés. Veuillez ne pas utiliser cette propriété pour prendre des décisions critiques en matière de sécurité. C'est juste une petite aide pour les esprits curieux.
DOMPurify fonctionne techniquement également côté serveur avec Node.js. Notre support s'efforce de suivre le cycle de publication de Node.js.
L'exécution de DOMPurify sur le serveur nécessite la présence d'un DOM, ce qui n'est probablement pas une surprise. Habituellement, jsdom est l'outil de choix et nous vous recommandons fortement d'utiliser la dernière version de jsdom .
Pourquoi? Parce que les anciennes versions de jsdom sont connues pour être boguées d'une manière qui entraîne XSS même si DOMPurify fait tout correctement à 100%. Il existe des vecteurs d'attaque connus , par exemple jsdom v19.0.0 , qui sont corrigés dans jsdom v20.0.0 - et nous recommandons vraiment de garder jsdom à jour pour cette raison.
Veuillez également noter que des outils comme happy-dom existent mais ne sont pas considérés comme sûrs à ce stade. La combinaison de DOMPurify avec happy-dom n'est actuellement pas recommandée et conduira probablement à XSS.
En dehors de cela, vous pouvez utiliser DOMPurify sur le serveur. Probablement. Cela dépend vraiment de jsdom ou du DOM que vous utilisez côté serveur. Si vous pouvez vivre avec cela, voici comment cela fonctionne :
npm installer dompurify npm installer jsdom
Pour jsdom (veuillez utiliser une version à jour), ceci devrait faire l'affaire :
const createDOMPurify = require('dompurify');const { JSDOM } = require('jsdom');const window = new JSDOM('').window;const DOMPurify = createDOMPurify(window);const clean = DOMPurify.sanitize(' <b>Bonjour</b>');
Ou même ceci, si vous préférez travailler avec des importations :
importer { JSDOM } depuis 'jsdom'; importer DOMPurify depuis 'dompurify';const window = new JSDOM('').window;const purify = DOMPurify(window);const clean = purify.sanitize('<b>bonjour< /b>');
Si vous rencontrez des problèmes pour le faire fonctionner dans votre configuration spécifique, envisagez de consulter l'incroyable projet isomorphic-dompurify qui résout de nombreux problèmes que les gens pourraient rencontrer.
npm installer isomorphic-dompurify
importer DOMPurify depuis 'isomorphic-dompurify'; const clean = DOMPurify.sanitize('<s>bonjour</s>');
Bien sûr, il y a une démo ! Jouez avec DOMPurify
Tout d'abord, veuillez nous contacter immédiatement par e-mail afin que nous puissions travailler sur un correctif. Clé PGP
De plus, vous avez probablement droit à une prime aux bugs ! Les gens formidables de Fastmail utilisent DOMPurify pour leurs services et ont ajouté notre bibliothèque à leur champ d'application de bug bounty. Donc, si vous trouvez un moyen de contourner ou d’affaiblir DOMPurify, veuillez également consulter leur site Web et les informations sur le bug bounty.
À quoi ressemble un balisage purifié ? Eh bien, la démo le montre pour un grand nombre d'éléments désagréables. Mais montrons également quelques exemples plus petits !
DOMPurify.sanitize('<img src=x onerror=alert(1)//>'); // devient <img src="x">DOMPurify.sanitize('<svg><g/onload=alert(2)//<p>'); // devient <svg><g></g></svg>DOMPurify.sanitize('<p>abc<iframe//src=jAva	script:alert(3)>def</p>'); // devient <p>abc</p>DOMPurify.sanitize('<math><mi//xlink:href="data:x,<script>alert(4)</script>">'); // devient <math><mi></mi></math>DOMPurify.sanitize('<TABLE><tr><td>HELLO</tr></TABL>'); // devient <table><tbody><tr><td>BONJOUR</td></tr></tbody></table>DOMPurify.sanitize('<UL><li><A HREF=//google .com>cliquez</UL>'); // devient un <ul><li><a href="//google.com">clic</a></li></ul>
DOMPurify prend actuellement en charge HTML5, SVG et MathML. DOMPurify autorise par défaut les attributs de données personnalisés CSS et HTML. DOMPurify prend également en charge le Shadow DOM et nettoie les modèles DOM de manière récursive. DOMPurify vous permet également de nettoyer le HTML pour qu'il soit utilisé avec les API jQuery $()
et elm.html()
sans aucun problème connu.
DOMPurify ne fait rien du tout. Il renvoie simplement exactement la chaîne que vous lui avez fournie. DOMPurify expose une propriété appelée isSupported
, qui vous indique si elle sera capable de faire son travail, afin que vous puissiez élaborer votre propre plan de sauvegarde.
Dans la version 1.0.9, la prise en charge de l'API Trusted Types a été ajoutée à DOMPurify. Dans la version 2.0.0, un indicateur de configuration a été ajouté pour contrôler le comportement de DOMPurify à ce sujet.
Lorsque DOMPurify.sanitize
est utilisé dans un environnement où l'API Trusted Types est disponible et RETURN_TRUSTED_TYPE
est défini sur true
, il essaie de renvoyer une valeur TrustedHTML
au lieu d'une chaîne (le comportement des options de configuration RETURN_DOM
et RETURN_DOM_FRAGMENT
ne change pas).
Notez que pour créer une stratégie dans trustedTypes
à l'aide de DOMPurify, RETURN_TRUSTED_TYPE: false
est requis, car createHTML
attend une chaîne normale, pas TrustedHTML
. L’exemple ci-dessous le montre.
window.trustedTypes!.createPolicy('default', { createHTML : (to_escape) =>DOMPurify.sanitize(to_escape, { RETURN_TRUSTED_TYPE : false }),});
Oui. Les valeurs de configuration par défaut incluses sont déjà assez bonnes - mais vous pouvez bien sûr les remplacer. Consultez le dossier /demos
pour voir de nombreux exemples sur la façon dont vous pouvez personnaliser DOMPurify.
// supprime {{ ... }}, ${ ... } et <% ... %> pour rendre la sortie sécurisée pour les systèmes de modèles// soyez prudent, ce mode n'est pas recommandé pour une utilisation en production.// permettant l'analyse des modèles en HTML contrôlé par l'utilisateur n'est pas du tout conseillée.// n'utilisez ce mode que s'il n'y a vraiment pas d'alternative.const clean = DOMPurify.sanitize(dirty, {SAFE_FOR_TEMPLATES: true});// changez la façon dont, par exemple, les commentaires contenant des risques Les caractères HTML sont traité.// soyez très prudent, ce paramètre ne doit être défini sur `false` que si vous ne manipulez vraiment que // du HTML et rien d'autre, pas de SVG, MathML ou autre. // Sinon, passer de `true` à `false` conduira à XSS de cette manière ou d'une autre manière.const clean = DOMPurify.sanitize(dirty, {SAFE_FOR_XML: false});
// autorise uniquement les éléments <b>, très strictconst clean = DOMPurify.sanitize(dirty, {ALLOWED_TAGS: ['b']});// autorise uniquement <b> et <q> avec les attributs de style const clean = DOMPurify.sanitize( dirty, {ALLOWED_TAGS : ['b', 'q'], ALLOWED_ATTR : ['style']});// autorise tous les éléments HTML sécurisés mais ni SVG ni MathML// notez que le paramètre USE_PROFILES remplacera le paramètre ALLOWED_TAGS// donc ne les utilisez pas ensembleconst clean = DOMPurify.sanitize(dirty, {USE_PROFILES: {html: true}});// autorise tous les éléments SVG sécurisés et Filtres SVG, pas de HTML ou MathMLconst clean = DOMPurify.sanitize(dirty, {USE_PROFILES : {svg : true, svgFilters: true}});// autoriser tous les éléments MathML et SVG sécurisés, mais pas de SVG Filtersconst clean = DOMPurify.sanitize(dirty, {USE_PROFILES: {mathMl: true, svg: true}});// modifier l'espace de noms par défaut du HTML à quelque chose de différentconst clean = DOMPurify.sanitize(dirty, {NAMESPACE : 'http://www.w3.org/2000/svg'});// laissez tout le HTML sécurisé tel quel et ajoutez des éléments <style> à block-listconst clean = DOMPurify.sanitize(dirty, {FORBID_TAGS: [' style']});// laissez tout le code HTML sécurisé tel quel et ajoutez des attributs de style à block-listconst clean = DOMPurify.sanitize(dirty, {FORBID_ATTR : ['style']});// étendre le tableau existant de balises autorisées et ajouter <my-tag> à allow-listconst clean = DOMPurify.sanitize(dirty, {ADD_TAGS: ['my-tag']});/ / étend le tableau existant des attributs autorisés et ajoute my-attr à allow-listconst clean = DOMPurify.sanitize(dirty, {ADD_ATTR: ['my-attr']});// interdire Attributs ARIA, laissez les autres attributs HTML sécurisés tels quels (vrai par défaut) const clean = DOMPurify.sanitize (dirty, {ALLOW_ARIA_ATTR: false});// interdisez les attributs de données HTML5, laissez les autres attributs HTML sécurisés tels quels (vrai par défaut) const clean = DOMPurify.sanitize(dirty, {ALLOW_DATA_ATTR : false});
// DOMPurify permet de définir des règles pour les éléments personnalisés. Lors de l'utilisation du littéral CUSTOM_ELEMENT_HANDLING//, il est possible de définir exactement quels éléments vous souhaitez autoriser (par défaut, aucun n'est autorisé).//// Il en va de même pour leurs attributs. Par défaut, la liste autorisée intégrée ou configurée est utilisée.//// Vous pouvez utiliser un littéral RegExp pour spécifier ce qui est autorisé ou un prédicat, des exemples pour les deux peuvent être vus ci-dessous.// Les valeurs par défaut sont très restrictives pour éviter les contournements XSS accidentels. À manipuler avec beaucoup de soin !const clean = DOMPurify.sanitize('<foo-bar baz="foobar" interdit="true"></foo-bar><div is="foo-baz"></div>', {CUSTOM_ELEMENT_HANDLING : {tagNameCheck : null, // aucun élément personnalisé n'est autoriséattributeNameCheck : null, // la liste autorisée des attributs par défaut/standard est utiliséeallowCustomizedBuiltInElements : false, // non personnalisée éléments intégrés autorisés},}); // <div is=""></div>const clean = DOMPurify.sanitize('<foo-bar baz="foobar" interdit="true"></foo-bar><div is="foo-baz "></div>',{CUSTOM_ELEMENT_HANDLING : {tagNameCheck : /^foo-/, // autorise toutes les balises commençant par "foo-"attributeNameCheck : /baz/, // autorise tous les attributs contenant "baz"allowCustomizedBuiltInElements : true, // les éléments intégrés personnalisés sont autorisés},}); // <foo-bar baz="foobar"></foo-bar><div is="foo-baz"></div>const clean = DOMPurify.sanitize('<foo-bar baz="foobar" interdit ="true"></foo-bar><div is="foo-baz"></div>',{CUSTOM_ELEMENT_HANDLING : {tagNameCheck : (tagName) => tagName.match(/^foo-/), // autorise toutes les balises commençant par "foo-"attributeNameCheck : (attr) => attr.match(/baz/), // autorise toutes les balises contenant "baz"allowCustomizedBuiltInElements : true, // autorise les éléments intégrés personnalisés},}); // <foo-bar baz="foobar"></foo-bar><div is="foo-baz"></div>
// étend le tableau existant d'éléments pouvant utiliser Data URIsconst clean = DOMPurify.sanitize(dirty, {ADD_DATA_URI_TAGS: ['a', 'area']});// étend le tableau existant d'éléments sûrs pour les URI- comme les valeurs (attention, risque XSS)const clean = DOMPurify.sanitize(dirty, {ADD_URI_SAFE_ATTR : ['mon-attr']});
// autorise les gestionnaires de protocole externes dans les attributs d'URL (la valeur par défaut est false, attention, risque XSS)// par défaut, seuls http, https, ftp, ftps, tel, mailto, callto, sms, cid et xmpp sont autorisés.const clean = DOMPurify.sanitize(dirty, {ALLOW_UNKNOWN_PROTOCOLS: true});// autoriser des gestionnaires de protocoles spécifiques dans les attributs d'URL via regex (la valeur par défaut est false, attention, risque XSS)// par défaut seuls http, https, ftp, ftps, tel, mailto, callto, sms, cid et xmpp sont autorisés.// RegExp par défaut : /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^az]|[a-z+.-]+(?:[^ a-z+.-:]|$))/i;const clean = DOMPurify.sanitize(dirty, {ALLOWED_URI_REGEXP : /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|xxx):|[^az]|[a-z+.-]+(?: [^a-z+.-:]|$))/i});
// renvoie un DOM HTMLBodyElement au lieu d'une chaîne HTML (la valeur par défaut est false) const clean = DOMPurify.sanitize (dirty, {RETURN_DOM: true});// renvoie un DOM DocumentFragment au lieu d'une chaîne HTML (la valeur par défaut est false) const clean = DOMPurify.sanitize(dirty, {RETURN_DOM_FRAGMENT: true});// utiliser le Indicateur RETURN_TRUSTED_TYPE pour activer la prise en charge des types de confiance si disponibleconst clean = DOMPurify.sanitize(dirty, {RETURN_TRUSTED_TYPE : true}); // renverra un objet TrustedHTML au lieu d'une chaîne si possible// utilisera une politique de types de confiance fournie return s},createScriptURL(s) { return s},}});
// renvoie le document entier, y compris les balises <html> (la valeur par défaut est false) const clean = DOMPurify.sanitize (dirty, {WHOLE_DOCUMENT: true}); // désactive la protection contre le DOM Clobbering en sortie (la valeur par défaut est true, à manipuler avec précaution, XSS mineur risques ici)const clean = DOMPurify.sanitize(dirty, {SANITIZE_DOM: false});// appliquer une protection stricte contre le DOM Clobbering via l'isolation de l'espace de noms (la valeur par défaut est false)// lorsqu'il est activé, isole l'espace de noms des propriétés nommées (c'est-à-dire les attributs `id` et `name`)// des variables JS en les préfixant avec la chaîne `user-content-`const clean = DOMPurify. sanitize(dirty, {SANITIZE_NAMED_PROPS: true});// conserver le contenu d'un élément lorsque l'élément est supprimé (la valeur par défaut est true)const clean = DOMPurify.sanitize(dirty, {KEEP_CONTENT: false});// colle des éléments comme le style, le script ou autres à document.body et empêche le comportement non intuitif du navigateur dans plusieurs cas extrêmes (la valeur par défaut est false) const clean = DOMPurify.sanitize(dirty , {FORCE_BODY : true});// supprime tous les éléments <a> sous les éléments <p> qui sont supprimésconst clean = DOMPurify.sanitize(dirty, {FORBID_CONTENTS : ['a'], FORBID_TAGS : ['p']});// modifier le type d'analyseur afin que les données nettoyées soient traitées comme XML et non comme HTML, ce qui est la valeur par défaut clean = DOMPurify .sanitize(dirty, {PARSER_MEDIA_TYPE : 'application/xhtml+xml'});
// utilise le mode IN_PLACE pour nettoyer un nœud "en place", ce qui est beaucoup plus rapide selon la façon dont vous utilisez DOMPurifyconst dirty = document.createElement('a');dirty.setAttribute('href', 'javascript:alert(1 )');const clean = DOMPurify.sanitize(dirty, {IN_PLACE : true}); // voir https://github.com/cure53/DOMPurify/issues/288 pour plus d'informations
Il y a encore plus d'exemples ici, montrant comment vous pouvez exécuter, personnaliser et configurer DOMPurify pour répondre à vos besoins.
Au lieu de transmettre à plusieurs reprises la même configuration à DOMPurify.sanitize
, vous pouvez utiliser la méthode DOMPurify.setConfig
. Votre configuration persistera jusqu'à votre prochain appel à DOMPurify.setConfig
, ou jusqu'à ce que vous appeliez DOMPurify.clearConfig
pour la réinitialiser. N'oubliez pas qu'il n'y a qu'une seule configuration active, ce qui signifie qu'une fois définie, tous les paramètres de configuration supplémentaires transmis à DOMPurify.sanitize
sont ignorés.
DOMPurify vous permet d'augmenter ses fonctionnalités en attachant une ou plusieurs fonctions avec la méthode DOMPurify.addHook
à l'un des hooks suivants :
beforeSanitizeElements
uponSanitizeElement
(Non 's' - appelé pour chaque élément)
afterSanitizeElements
beforeSanitizeAttributes
uponSanitizeAttribute
afterSanitizeAttributes
beforeSanitizeShadowDOM
uponSanitizeShadowNode
afterSanitizeShadowDOM
Il transmet le nœud DOM actuellement traité, si nécessaire, un littéral avec les données de nœud et d'attribut vérifiées et la configuration DOMPurify au rappel. Consultez la démo du hook MentalJS pour voir comment l'API peut être utilisée correctement.
Exemple :
DOMPurify.addHook( 'surSanitizeAttribute', function (currentNode, hookEvent, config) {// Faire quelque chose avec le nœud actuel // Vous pouvez également muter hookEvent pour le nœud actuel (c'est-à-dire définir hookEvent.forceKeepAttr = true)// Pour les types de hook autres que 'uponSanitizeAttribute', hookEvent est égal à null });
Option | Depuis | Note |
---|---|---|
SAFE_FOR_JQUERY | 2.1.0 | Aucun remplacement requis. |
Nous utilisons actuellement Github Actions en combinaison avec BrowserStack. Cela nous donne la possibilité de confirmer pour chaque commit que tout se passe comme prévu dans tous les navigateurs pris en charge. Consultez les journaux de construction ici : https://github.com/cure53/DOMPurify/actions
Vous pouvez également exécuter des tests locaux en exécutant npm test
. Les tests fonctionnent correctement avec Node.js v0.6.2 et [email protected].
Tous les commits pertinents seront signés avec la clé 0x24BB6BF4
pour plus de sécurité (depuis le 8 avril 2016).
npm i
) Nous soutenons officiellement npm
. Le workflow GitHub Actions est configuré pour installer les dépendances à l'aide de npm
. Lors de l'utilisation d'une version obsolète de npm
nous ne pouvons pas garantir pleinement les versions des dépendances installées, ce qui pourrait entraîner des problèmes imprévus.
Nous nous appuyons sur les scripts d'exécution npm pour l'intégration à notre infrastructure d'outils. Nous utilisons ESLint comme hook de pré-validation pour garantir la cohérence du code. De plus, pour faciliter le formatage, nous utilisons plus joli lors de la création des actifs /dist
via rollup
.
Voici nos scripts npm :
npm run dev
pour commencer à construire tout en surveillant les sources pour les changements
npm run test
pour exécuter notre suite de tests via jsdom et karma
test:jsdom
pour exécuter uniquement des tests via jsdom
test:karma
pour exécuter uniquement des tests via karma
npm run lint
pour lint les sources en utilisant ESLint (via xo)
npm run format
pour formater nos sources en utilisant plus joli pour faciliter le passage d'ESLint
npm run build
pour créer nos actifs de distribution minifiés et non minifiés en tant que module UMD
npm run build:umd
pour construire uniquement un module UMD non minifié
npm run build:umd:min
pour construire uniquement un module UMD minifié
Remarque : tous les scripts d'exécution déclenchés via npm run <script>
.
Il existe plus de scripts npm mais ils sont principalement destinés à s'intégrer à CI ou sont censés être "privés", par exemple pour modifier les fichiers de distribution de build à chaque validation.
Nous ma