Les Trusted Types sont une fonctionnalité de sécurité qui permet d'éviter les attaques par script intersites basées sur le DOM (DOM XSS). Pour cela, ils exigent que les données transmises à des API de navigateur "dangereuses" (appelées "sinks") soient d'abord converties en un type spécifique, tel que TrustedHTML.
Par défaut, de nombreuses API DOM (comme innerHTML ou document.write) acceptent les chaînes brutes. Si une chaîne contient un script malveillant provenant d'une source non fiable, elle peut être exécutée. Les Trusted Types modifient ce comportement : une fois activés via la CSP, ces récepteurs n'accepteront que les objets TrustedHTML créés par des règles autorisées.
Principaux détails de l'implémentation
Pour utiliser les types de confiance, suivez quatre étapes principales :
- Définissez une stratégie de sécurité qui spécifie comment nettoyer ou transformer les chaînes.
- Utilisez cette règle pour créer des objets Trusted Types à partir de chaînes brutes.
- Utilisez les objets Trusted Types lorsque vous écrivez dans des récepteurs DOM.
- Appliquez les Trusted Types à l'aide d'une Content Security Policy (CSP).
1. Créer une règle
Une règle est un ensemble de règles permettant de nettoyer le contenu. Vous la créez à l'aide de trustedTypes.createPolicy(), en déléguant généralement à une bibliothèque de nettoyage telle que DOMPurify ou aux règles d'échappement HTML de votre application :
import DOMPurify from 'dompurify';
const myPolicy = trustedTypes.createPolicy('sanitize-html', {
createHTML: (input) =>
// Return a sanitized string; createPolicy() wraps it in a TrustedHTML object.
// Example using DOMPurify:
DOMPurify.sanitize(input, { RETURN_TRUSTED_TYPE: false }),
});
// Example using HTML escaping (for environments without third-party sanitizers):
const escapePolicy = trustedTypes.createPolicy('escape-html', {
createHTML: (input) =>
String(input)
.replaceAll('&', '&')
.replaceAll('<', '<')
.replaceAll('>', '>')
.replaceAll('"', '"')
.replaceAll("'", '''),
});
2. Utiliser la règle
Au lieu de transmettre directement une chaîne à un récepteur, vous appelez la méthode de création de la règle.
const untrustedInput = '<p>Hello, <strong>world</strong>!</p><img src="x" onerror="alert(1)">';
// In an enforced environment, this would throw a TypeError:
// element.innerHTML = untrustedInput;
// This returns a TrustedHTML object with unsafe elements and attributes removed:
const cleanHTML = myPolicy.createHTML(untrustedInput);
// Result: "<p>Hello, <strong>world</strong>!</p><img src=\"x\">"
3. Transition des récepteurs vers l'utilisation des Trusted Types
Mettez à jour chaque instance où vous écrivez dans un récepteur pour la convertir d'abord en un objet Trusted Type. Pour localiser plus facilement les endroits où le code doit être mis à jour, vous pouvez définir une règle default temporaire qui consigne les écritures dans un récepteur avec une chaîne plutôt qu'un TrustedType.
trustedTypes.createPolicy("default", {
createHTML(value) {
console.warn("String passed instead of Trusted Type.");
return DOMPurify.sanitize(value, { RETURN_TRUSTED_TYPE: false });
},
});
Voici quelques récepteurs courants compatibles avec les types de confiance :
- HTML :
element.innerHTML,element.outerHTML,document.write(),element.setHTMLUnsafe(),Document.parseHTMLUnsafe() - Script : contenu textuel
<script>,eval() - ScriptURL :
<script src>,Worker(),SharedWorker()
// Assign the TrustedHTML object.
element.innerHTML = cleanHTML;
Notez que les méthodes de nettoyage HTML "sûres" (element.setHTML() et Document.parseHTML(), décrites dans Nettoyer le code HTML non fiable) ne nécessitent pas de règle Trusted Types, car elles nettoient toujours les attaques XSS potentielles de manière native.
4. Appliquer les Trusted Types avec CSP
Les Trusted Types doivent être déployés de manière incrémentielle pour éviter de perturber le fonctionnement de votre application. Bien que l'application complète offre la protection la plus efficace, elle comporte un risque de dysfonctionnement du site si toutes les écritures de récepteur n'ont pas été migrées.
Prérequis
- Coopération du framework : si le framework de votre application (ou toute bibliothèque tierce qui écrit dans des récepteurs DOM) ne produit pas de valeurs
TrustedHTMLouTrustedScript, vous ne pouvez pas appliquer les types de confiance sans endommager ce code. Vérifiez la compatibilité du framework avant de commencer le déploiement. - Refactorisation des récepteurs : assurez-vous d'avoir identifié et refactorisé les récepteurs dangereux pour utiliser vos règles de types de confiance.
Stratégie de déploiement
Rapport uniquement : commencez par déployer la règle à l'aide de l'en-tête
Content-Security-Policy-Report-Only. Cela enregistrera les cas de non-conformité dans votre point de terminaison de reporting sans bloquer les écritures.Content-Security-Policy-Report-Only: require-trusted-types-for 'script';Application : une fois que vous avez intégré vos règles et que les rapports de non-respect sont nuls, passez à l'application complète.
Content-Security-Policy: require-trusted-types-for 'script';
Vous pouvez également limiter les règles autorisées à être créées (répertoriez les noms de vos règles, tels que sanitize-html ou escape-html, incluez dompurify si votre règle délègue à DOMPurify.sanitize(), ce qui crée en interne une règle dompurify, et incluez default si vous enregistrez une règle par défaut lors de la migration) :
Content-Security-Policy: require-trusted-types-for 'script'; trusted-types sanitize-html escape-html dompurify;
Avec cette CSP en place, toute écriture dans un récepteur qui n'utilise pas les types de confiance génère une erreur. De plus, toute tentative de création d'une règle de type de confiance avec un nom ne figurant pas dans trusted-types générera une erreur.
Vous pouvez également définir cette CSP en tant que balise <meta> dans l'élément <head> de votre document, bien que cela soit potentiellement moins sécurisé.
<!-- First child of `<head>`, as earlier tags will not enforce this CSP. -->
<meta
http-equiv="Content-Security-Policy"
content="require-trusted-types-for 'script'; trusted-types sanitize-html escape-html dompurify;"
/>
Stratégies de remplacement
Pour les navigateurs qui ne sont pas encore compatibles avec les types de confiance, vous pouvez utiliser un "tinyfill" minimal pour vous assurer que votre code ne se brise pas tout en assurant le nettoyage.
if (!window.trustedTypes) {
window.trustedTypes = {
createPolicy: (name, rules) => rules,
};
}
Cela vous permet d'écrire votre code en utilisant le modèle Trusted Types dès aujourd'hui. Les navigateurs compatibles appliqueront les types, tandis que les navigateurs plus anciens exécuteront toujours votre logique de nettoyage via la règle fictive.
Si un navigateur n'est pas compatible avec les types de confiance, il traitera simplement les objets TrustedHTML comme des chaînes (si vous utilisez le tinyfill) ou n'effectuera pas de vérification d'application au niveau du récepteur.