Prévenez les failles des scripts DOM basés sur des scripts intersites grâce aux Trusted Types

Réduisez la surface d'attaque DOM XSS de votre application.

Krzysztof Kotowicz
Krzysztof Kotowicz

Quel est l'intérêt pour vous ?

Les scripts intersites basés sur DOM (DOM XSS) sont l'une des failles de sécurité Web les plus courantes. Il est très facile de l'intégrer à votre application. Les Trusted Types vous offrent les outils nécessaires pour rédiger, examiner et gérer les applications, et exempter les failles DOM XSS de ces failles en sécurisant par défaut les fonctions dangereuses des API Web. Les Trusted Types sont compatibles avec Chrome 83 et un polyfill est disponible pour d'autres navigateurs. Pour obtenir des informations à jour sur la compatibilité entre les navigateurs, consultez la section Compatibilité du navigateur.

Contexte

Depuis de nombreuses années, DOM XSS est l'une des failles de sécurité Web les plus répandues et les plus dangereuses.

Il existe deux groupes distincts de script intersites. Certaines failles XSS sont causées par le code côté serveur qui crée de manière non sécurisée le code HTML qui forme le site Web. D'autres ont une cause première sur le client : le code JavaScript appelle des fonctions dangereuses avec du contenu contrôlé par l'utilisateur.

Pour empêcher le XSS côté serveur, ne générez pas de code HTML en concaténant des chaînes et utilisez plutôt des bibliothèques de modèles d'échappement contextuel sécurisées. Utilisez une Règle de sécurité du contenu basée sur des nonce pour réduire davantage les bugs qui surviennent inévitablement.

Désormais, un navigateur peut également éviter les XSS côté client (également appelés fichiers XSS basés sur DOM) grâce aux Trusted Types.

Présentation de l'API

Les Trusted Types verrouillent les fonctions de récepteur à risque suivantes. Vous reconnaissez peut-être déjà certains d'entre eux, car les fournisseurs de navigateurs et les frameworks Web vous empêchent déjà d'utiliser ces fonctionnalités pour des raisons de sécurité.

Les Trusted Types nécessitent que vous traitez les données avant de les transmettre aux fonctions de récepteur ci-dessus. Le simple fait d'utiliser une chaîne échouera, car le navigateur ne pourra pas savoir si les données sont fiables:

À éviter
anElement.innerHTML  = location.href;
Lorsque les Trusted Types sont activés, le navigateur génère une erreur TypeError et empêche l'utilisation d'un récepteur XSS DOM avec une chaîne.

Pour indiquer que les données ont été traitées de manière sécurisée, créez un objet spécial : Trusted Type.

À faire
anElement.innerHTML = aTrustedHTML;
Lorsque les Trusted Types sont activés, le navigateur accepte un objet TrustedHTML pour les récepteurs qui attendent des extraits HTML. Il existe également des objets TrustedScript et TrustedScriptURL pour d'autres récepteurs sensibles.

Les Trusted Types réduisent considérablement la surface d'attaque DOM XSS de votre application. Il simplifie les examens de sécurité et vous permet d'appliquer les contrôles de sécurité basés sur le type effectués lors de la compilation, du linting ou du regroupement de votre code au moment de l'exécution, dans le navigateur.

Utiliser les Trusted Types

Se préparer aux rapports sur les cas de non-respect de Content Security Policy

Vous pouvez déployer un collecteur de rapports (tel que le go-csp-collector Open Source) ou utiliser l'un des équivalents commerciaux. Vous pouvez également déboguer les cas de non-respect dans le navigateur : js document.addEventListener('securitypolicyviolation', console.error.bind(console));

Ajouter un en-tête CSP pour rapport uniquement

Ajoutez l'en-tête de réponse HTTP suivant aux documents que vous souhaitez migrer vers les Trusted Types. text Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example

Tous les cas de non-respect sont désormais signalés à //my-csp-endpoint.example, mais le site Web continue de fonctionner. La section suivante explique le fonctionnement de //my-csp-endpoint.example.

Identifier les infractions aux Trusted Types

Désormais, chaque fois que les Trusted Types détectent un cas de non-respect, un rapport est envoyé à un report-uri configuré. Par exemple, lorsque votre application transmet une chaîne à innerHTML, le navigateur envoie le rapport suivant:

{
"csp-report": {
    "document-uri": "https://my.url.example",
    "violated-directive": "require-trusted-types-for",
    "disposition": "report",
    "blocked-uri": "trusted-types-sink",
    "line-number": 39,
    "column-number": 12,
    "source-file": "https://my.url.example/script.js",
    "status-code": 0,
    "script-sample": "Element innerHTML <img src=x"
}
}

Cela indique que dans https://my.url.example/script.js, à la ligne 39, innerHTML a été appelé avec la chaîne commençant par <img src=x. Ces informations devraient vous aider à déterminer quelles parties du code peuvent introduire du DOM XSS et qu'elles doivent être modifiées.

Corriger les cas de non-respect

Deux options s'offrent à vous pour corriger un cas de non-respect du type de confiance. Vous pouvez supprimer le code incriminé, utiliser une bibliothèque, créer une règle de type de confiance ou, en dernier recours, créer une règle par défaut.

Réécrire le code incriminé

Peut-être que la fonctionnalité non conforme n'est plus nécessaire ou peut être réécrite de manière moderne sans utiliser les fonctions sujettes aux erreurs ?

À éviter
el.innerHTML = '';
À faire
el.textContent = '';
const img = document.createElement('img');
img.src = 'xyz.jpg';
el.appendChild(img);

Utiliser une bibliothèque

Certaines bibliothèques génèrent déjà des Trusted Types que vous pouvez transmettre aux fonctions du récepteur. Par exemple, vous pouvez utiliser DOMPurify pour nettoyer un extrait HTML, en supprimant les charges utiles XSS.

import DOMPurify from 'dompurify';
el.innerHTML = DOMPurify.sanitize(html, {RETURN_TRUSTED_TYPE: true});

DOMPurify accepte les Trusted Types et renvoie un code HTML nettoyé encapsulé dans un objet TrustedHTML, de sorte que le navigateur ne génère pas de violation.

Créer une règle Trusted Type

Parfois, il n'est pas possible de supprimer la fonctionnalité et il n'y a pas de bibliothèque pour nettoyer la valeur et créer un type de confiance pour vous. Dans ce cas, créez vous-même un objet Trusted Type.

Pour cela, commencez par créer une règle. Les règles sont des fabriques de Trusted Types qui appliquent certaines règles de sécurité lors de leur saisie:

if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
  const escapeHTMLPolicy = trustedTypes.createPolicy('myEscapePolicy', {
    createHTML: string => string.replace(/\</g, '&lt;')
  });
}

Ce code crée une règle appelée myEscapePolicy qui peut produire des objets TrustedHTML via sa fonction createHTML(). Les règles définies intègrent un échappement HTML pour les caractères < afin d'empêcher la création d'éléments HTML.

Utilisez la règle comme suit:

const escaped = escapeHTMLPolicy.createHTML('<img src=x onerror=alert(1)>');
console.log(escaped instanceof TrustedHTML);  // true
el.innerHTML = escaped;  // '&lt;img src=x onerror=alert(1)>'

Utiliser une règle par défaut

Parfois, vous ne pouvez pas modifier le code incriminé. C'est par exemple le cas si vous chargez une bibliothèque tierce à partir d'un CDN. Dans ce cas, utilisez une règle par défaut:

if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
  trustedTypes.createPolicy('default', {
    createHTML: (string, sink) => DOMPurify.sanitize(string, {RETURN_TRUSTED_TYPE: true})
  });
}

La règle nommée default est utilisée chaque fois qu'une chaîne est utilisée dans un récepteur qui n'accepte que le type de confiance.

Passer à l'application de Content Security Policy

Lorsque votre application ne génère plus de cas de non-respect, vous pouvez commencer à appliquer les Trusted Types:

Content-Security-Policy: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example

Simple comme "Bonjour" ! Désormais, quelle que soit la complexité de votre application Web, la seule chose qui puisse introduire une faille DOM XSS est le code de l'une de vos règles. Vous pouvez verrouiller davantage cette faille en limitant la création de règles.

Documentation complémentaire