Un script intersites basé sur DOM (DOM XSS) se produit lorsque les données d'une source contrôlée par l'utilisateur (comme un nom d'utilisateur ou une URL de redirection extraite du fragment d'URL) atteignent un récepteur, qui est une fonction telle que eval()
ou un setter de propriété comme .innerHTML
qui peut exécuter du code JavaScript arbitraire.
Le DOM XSS est l'une des failles de sécurité Web les plus courantes, et il est courant que les équipes de développement l'introduisent accidentellement dans leurs applications. Les Trusted Types vous offrent les outils nécessaires pour rédiger du texte, examiner les failles de sécurité et protéger les applications des failles DOM XSS en sécurisant par défaut les fonctions dangereuses des API Web. Les Trusted Types sont disponibles sous forme de polyfill pour les navigateurs qui ne les prennent pas encore en charge.
Contexte
Depuis de nombreuses années, DOM XSS est l'une des failles de sécurité Web les plus courantes et les plus dangereuses.
Il existe deux types de script intersites. Certaines failles XSS sont causées par du code côté serveur qui crée de manière non sécurisée le code HTML formant 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. Utilisez plutôt des bibliothèques de modèles d'échappement automatique contextuel sécurisées et une stratégie de sécurité du contenu basée sur des nonces pour mieux atténuer les bugs.
Désormais, les navigateurs peuvent également empêcher les scripts XSS basés sur le DOM côté client en utilisant les Trusted Types.
Présentation de l'API
Les Trusted Types verrouillent les fonctions de récepteur à risque suivantes. Vous reconnaîtrez 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é.
- Manipulation de script:
<script src>
et définition du contenu textuel des éléments<script>
. - Générer du code HTML à partir d'une chaîne :
- Exécution du contenu du plug-in :
- Compilation JavaScript d'exécution :
eval
setTimeout
setInterval
new Function()
Les Trusted Types nécessitent que vous traitez les données avant de les transmettre à ces fonctions de récepteur. L'utilisation d'une seule chaîne échoue, car le navigateur ne sait pas si les données sont fiables:
anElement.innerHTML = location.href;
Pour indiquer que les données ont été traitées de manière sécurisée, créez un objet spécial : Trusted Type.
anElement.innerHTML = aTrustedHTML;
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 dans le navigateur 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.
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 système Open Source go-csp-collector, ou utiliser l'un des équivalents commerciaux. Vous pouvez également déboguer les cas de non-respect dans le navigateur:
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:
Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
Toutes les infractions sont désormais signalées à //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, le navigateur envoie un rapport à 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 mis en cause, 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é
Il est possible que le code non conforme ne soit plus nécessaire ou qu'il soit réécrit sans les fonctions à l'origine des cas de non-respect:
el.textContent = ''; const img = document.createElement('img'); img.src = 'xyz.jpg'; el.appendChild(img);
el.innerHTML = '';
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 de code HTML et supprimer les charges utiles XSS.
import DOMPurify from 'dompurify';
el.innerHTML = DOMPurify.sanitize(html, {RETURN_TRUSTED_TYPE: true});
DOMPurify accepte les Trusted Types et renvoie du code HTML nettoyé encapsulé dans un objet TrustedHTML
afin que le navigateur ne génère pas de violation.
Créer une règle Trusted Type
Parfois, vous ne pouvez pas supprimer le code à l'origine du non-respect et aucune bibliothèque ne permet de nettoyer la valeur et de créer un type de confiance pour vous. Dans ce cas, vous pouvez créer vous-même un objet Trusted Type.
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 entrée:
if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
const escapeHTMLPolicy = trustedTypes.createPolicy('myEscapePolicy', {
createHTML: string => string.replace(/\</g, '<')
});
}
Ce code crée une stratégie appelée myEscapePolicy
qui peut produire des objets TrustedHTML
à l'aide de sa fonction createHTML()
. Les règles définies contiennent des caractères d'échappement HTML (<
) pour 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; // '<img src=x onerror=alert(1)>'
Utiliser une règle par défaut
Parfois, vous ne pouvez pas modifier le code incriminé, par exemple 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 approuvé.
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
Désormais, quelle que soit la complexité de votre application Web, la seule chose qui peut 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.