Le script intersites basé sur le DOM (DOM XSS) se produit lorsque des données provenant 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 comme eval()
ou un setter de propriété comme .innerHTML
pouvant exécuter du code JavaScript arbitraire.
Le DOM XSS est l'une des failles de sécurité Web les plus courantes. Il arrive souvent que les équipes de développement l'introduisent accidentellement dans leurs applications. Les Trusted Types vous fournissent les outils nécessaires pour écrire, examiner la sécurité et protéger les applications contre les failles DOM XSS en sécurisant par défaut les fonctions d'API Web dangereuses. Les types fiables sont disponibles sous forme de polyfill pour les navigateurs qui ne les prennent pas encore en charge.
Arrière-plan
Pendant de nombreuses années, le DOM XSS a été l'une des failles de sécurité Web les plus répandues et les plus dangereuses.
Il existe deux types de scripts 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 racine côté client, où le code JavaScript appelle des fonctions dangereuses avec du contenu contrôlé par l'utilisateur.
Pour éviter 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é, ainsi qu'une stratégie de sécurité du contenu basée sur un nonce pour atténuer davantage les bugs.
Les navigateurs peuvent désormais également contribuer à prévenir les attaques XSS basées sur le DOM côté client à l'aide des Trusted Types.
Présentation de l'API
Les types fiables fonctionnent en verrouillant les fonctions de récepteur risquées suivantes. Vous en reconnaîtrez peut-être déjà certains, car les fournisseurs de navigateurs et les frameworks Web vous dissuadent déjà de les utiliser pour des raisons de sécurité.
- Manipulation de scripts :
<script src>
et définition du contenu textuel des éléments<script>
. - Générer du code HTML à partir d'une chaîne :
- Exécuter le contenu du plug-in :
- Compilation du code JavaScript d'exécution :
eval
setTimeout
setInterval
new Function()
Les types fiables vous obligent à traiter les données avant de les transmettre à ces fonctions de récepteur. L'utilisation d'une chaîne seule é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 : un Trusted Type.
anElement.innerHTML = aTrustedHTML;
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 vérifications de sécurité basées sur les types effectuées lors de la compilation, de l'analyse ou de l'assemblage de votre code au moment de l'exécution, dans le navigateur.
Utiliser les types approuvés
Se préparer aux rapports sur les cas de non-respect de la Content Security Policy
Vous pouvez déployer un collecteur de rapports, tel que reporting-api-processor ou go-csp-collector (tous deux open source), ou utiliser l'un des équivalents commerciaux. Vous pouvez également ajouter une journalisation personnalisée et déboguer les cas de non-respect dans le navigateur à l'aide d'un ReportingObserver :
const observer = new ReportingObserver((reports, observer) => {
for (const report of reports) {
if (report.type !== 'csp-violation' ||
report.body.effectiveDirective !== 'require-trusted-types-for') {
continue;
}
const violation = report.body;
console.log('Trusted Types Violation:', violation);
// ... (rest of your logging and reporting logic)
}
}, { buffered: true });
observer.observe();
ou en ajoutant un écouteur d'événements :
document.addEventListener('securitypolicyviolation',
console.error.bind(console));
Ajouter un en-tête CSP en mode rapport uniquement
Ajoutez l'en-tête de réponse HTTP suivant aux documents que vous souhaitez migrer vers les types fiables :
Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
Toutes les violations sont désormais signalées à //my-csp-endpoint.example
, mais le site Web continue de fonctionner. La section suivante explique comment fonctionne //my-csp-endpoint.example
.
Identifier les cas de non-respect des Trusted Types
Désormais, chaque fois que les types fiables détectent une violation, 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 signifie 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 à identifier les parties du code qui pourraient introduire des XSS DOM et qui doivent être modifiées.
Corriger les cas de non-respect des règles
Vous pouvez résoudre un cas de non-respect des Trusted Types de deux manières. Vous pouvez supprimer le code qui pose problème, utiliser une bibliothèque, créer une règle Trusted Types 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 puisse être réécrit sans les fonctions qui entraînent les cas de non-respect :
el.textContent = ''; const img = document.createElement('img'); img.src = 'xyz.jpg'; el.appendChild(img);
el.innerHTML = '<img src=xyz.jpg>';
Utiliser une bibliothèque
Certaines bibliothèques génèrent déjà des types fiables que vous pouvez transmettre aux fonctions de récepteur. Par exemple, vous pouvez utiliser DOMPurify pour assainir un extrait HTML en supprimant les charges utiles XSS.
import DOMPurify from 'dompurify';
el.innerHTML = DOMPurify.sanitize(html, {RETURN_TRUSTED_TYPE: true});
DOMPurify est compatible avec les Trusted Types et renvoie du code HTML assaini encapsulé dans un objet TrustedHTML
afin que le navigateur ne génère pas de non-respect.
Créer une règle Trusted Types
Il arrive que vous ne puissiez pas supprimer le code à l'origine du non-respect et qu'aucune bibliothèque ne soit disponible pour assainir la valeur et créer un type fiable 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 usines pour les Trusted Types qui appliquent certaines règles de sécurité à 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 règle appelée myEscapePolicy
qui peut produire des objets TrustedHTML
à l'aide de sa fonction createHTML()
. Les règles définies échappent les caractères <
pour empêcher la création de nouveaux é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
Il arrive que vous ne puissiez 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 les types fiables.
Passer à l'application de la stratégie de sécurité du contenu
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 XSS DOM est le code de l'une de vos règles. Vous pouvez renforcer encore davantage la sécurité en limitant la création de règles.