Génie civil & structural
Comment utiliser Javascript pour détecter et prévenir les attaques de scripts inter-site (xss)
Table of Contents
Introduction au scripting sur site (XSS) et aux défenses JavaScript
Le scripting sur le site (XSS) est l'une des vulnérabilités de sécurité web les plus répandues, en gardant le classement dans le Top 10 de l'OWASP. Une attaque XSS permet à un attaquant d'injecter des scripts côté client malveillants dans des pages Web consultées par d'autres utilisateurs. Ces scripts peuvent voler des jetons de session, rediriger les utilisateurs vers des sites de phishing, des pages de déformation ou des logiciels malveillants.
Comprendre les trois types de XSS
Avant de plonger dans la prévention, il est essentiel de comprendre les trois principales catégories de XSS : stockées, réfléchies et basées sur DOM. Chacune nécessite une approche légèrement différente de détection et de prévention.
Stocké XSS
Stocké (persistant) XSS se produit lorsque l'entrée malveillante est stockée en permanence sur le serveur (par exemple, dans une base de données, un message de forum ou un commentaire) et servi ensuite aux utilisateurs sans sanitisation appropriée.
XSS réfléchi
Reflected XSS se produit lorsque le script malveillant est réfléchi sur un serveur Web, généralement via un paramètre URL ou une soumission de formulaire. L'attaquant assaille une victime en cliquant sur un lien conçu, et le code injecté s'exécute immédiatement. Contrairement à XSS stocké, la charge utile ne persiste pas.
XSS basé sur les DOM
La charge utile d'attaque modifie l'environnement DOM dans le navigateur de la victime. Le code malveillant ne touche jamais le serveur; il provient du JavaScript côté client qui gère dangereusement les entrées utilisateur (par exemple, lecture de , ou .
Détecter les attaques XSS avec JavaScript
JavaScript peut surveiller les entrées de l'utilisateur, suivre les mutations DOM et valider les données aux points d'entrée. Bien que la détection côté client ne puisse pas attraper toutes les attaques (surtout si l'attaquant demande directement au serveur), il fournit une première ligne de défense précieuse.
Validation et désinfection des entrées
Validez et désinfectez toujours les entrées utilisateur du côté client avant le traitement. Utilisez au lieu de pour empêcher l'exécution du script. La fonction suivante enlève les caractères dangereux d'une chaîne :
function sanitizeInput(input) {
const div = document.createElement('div');
div.textContent = input;
return div.innerHTML;
}
Cela fonctionne parce que le paramètre n'interprète pas les balises HTML; il traite tout comme du texte simple. Le résultant contient des versions échappées de tous les caractères spéciaux HTML (par exemple, , , .
Surveillance des mutations des DOM pour les éléments suspects
Les attaquants injectent souvent des balises ou des gestionnaires d'événements [, dans le DOM. En utilisant l'API , vous pouvez regarder des insertions inattendues d'éléments. Un exemple de base :
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === 1) { // element node
if (node.tagName === 'SCRIPT') {
console.warn('Potential XSS: a script element was injected via DOM.');
node.remove(); // or log and analyze
}
// Check for dangerous attributes
if (node.hasAttribute('onerror') || node.hasAttribute('onload')) {
console.warn('Suspicious event handler attribute detected.');
}
}
});
});
});
observer.observe(document.body, { childList: true, subtree: true });
Attention: Les scripts de blocage via peuvent être contournés par des attaquants intelligents et peuvent briser des fonctionnalités légitimes. Utilisez-les comme un outil de surveillance plutôt qu'un mécanisme de prévention primaire.
Validation des paramètres d'URL et de Hash
Pour XSS basé sur DOM, lisez les composants URL en toute sécurité en utilisant et évitez d'insérer directement des valeurs dans HTML.
const params = new URLSearchParams(window.location.search);
const userParam = params.get('name');
if (userParam && /[<>"'\/]/.test(userParam)) {
console.warn('Potential XSS in parameter: ' + userParam);
// Do not use this value in the DOM without encoding
}
Prévention des attaques XSS avec JavaScript
La prévention nécessite une approche multicouche. JavaScript seul ne peut pas garantir complètement une application, mais lorsqu'il est combiné avec une désinfection adéquate du moteur et Politique de sécurité du contenu (CSP)[, il réduit considérablement le risque.
Encoder toutes les données contrôlées par l'utilisateur avant d'insérer dans DOM
La règle d'or : ne jamais insérer de données non fiables directement dans le DOM. Utilisez des méthodes DOM sûres au lieu d'innerHTML.
Utiliser ou
const userInput = getUserInput();
const safeText = document.createTextNode(userInput);
document.getElementById('output').appendChild(safeText);
Quand vous devez utiliser , Sanitiser avec une bibliothèque
Si vous avez absolument besoin de rendre HTML (par exemple, à partir d'un éditeur de texte riche), comptez sur une bibliothèque de sanitisation de confiance comme DOMpurify.DOMpurify est une bibliothèque largement utilisée, éprouvée par la bataille qui supprime le code malveillant tout en préservant un HTML sûr.
// Example with DOMPurify (install via npm or CDN)
const dirty = '<img src=x onerror="alert(1)">';
const clean = DOMPurify.sanitize(dirty);
document.getElementById('content').innerHTML = clean;
DOMPurify fonctionne en analysant l'entrée, en dégraissant les étiquettes et attributs dangereux et en ne renvoyant que les éléments autorisés. Voir DOMPurify sur GitHub.
Évitez les fonctions JavaScript dangereuses
Certaines méthodes et propriétés JavaScript sont notoires pour activer XSS. Évitez ou contrôlez strictement:
- — utiliser ou bien bien s'assainir.
- , — même règle.
- — ne jamais utiliser avec l'entrée de l'utilisateur.
- — peut être exploité si une entrée est concaténée.
- / avec code à cordes[ — éviter; utiliser des références de fonction à la place.
- constructeur[ — analogue à .
Mettre en oeuvre la politique de sécurité du contenu (CSP) via JavaScript? Non recommandé
CSP est un mécanisme de navigateur qui limite les scripts qui peuvent fonctionner. Il est généralement défini via des en-têtes HTTP, mais vous pouvez aussi le définir en utilisant une balise ou via JavaScript en créant dynamiquement un élément . Cependant, le réglage de CSP dans JavaScript est moins sûr parce qu'un attaquant qui a déjà un contrôle pourrait le désactiver.
const meta = document.createElement('meta');
meta.httpEquiv = 'Content-Security-Policy';
meta.content = "default-src 'self'; script-src 'self' 'unsafe-inline'"; // Be very careful with 'unsafe-inline'
document.head.appendChild(meta);
Pour la production, configurer CSP dans votre serveur Web ou proxy inverse. MDN La documentation CSP fournit des conseils complets.
Mesures de sécurité supplémentaires
Au-delà des tactiques spécifiques à JavaScript, une stratégie complète de prévention du SSI comprend ces mesures essentielles :
- Validez toujours du côté du serveur. La validation côté client peut être contournée. Ne jamais faire confiance aux données du client.
- Utiliser les en-têtes de réponse HTTP appropriés. , , et surtout .
- Encoder les sorties à chaque fois que vous rendez des données utilisateur. Contexte important : encoder pour les entités HTML, encodage d'URL, encodage de chaîne JavaScript, etc.
- Gardez à jour les dépendances. Les bibliothèques JavaScript vulnérables (p. ex., versions antérieures de jQuery) sont un vecteur XSS commun. Utilisez l'audit npm ou des outils similaires.
- Utilisez des cadres avec une protection XSS intégrée. Réaction, Angulaire et Vue automatiquement la sortie d'échappement par défaut. Néanmoins, soyez prudent avec ou .
- Mise en œuvre d'un CSP strict. Éviter et si possible.
Exemple du monde réel : rendu de commentaires sécurisés
Considérez un système de commentaires de blog où les utilisateurs soumettent des messages qui sont affichés à d'autres. Un attaquant pourrait essayer d'insérer . Voici une approche JavaScript qui s'intègre au moteur:
- Présentation de la présentation de la version finale: Sanitize using before envoying to server (mais le serveur doit encore s'assainir).
- Serveur retourne les données:[ Le moteur de recherche doit coder le texte du commentaire en HTML.
- Rendre les clients:[ Utiliser ou un moteur de gabarit sûr. N'utilisez jamais avec des données brutes de l'utilisateur.
function renderComment(comment) {
const item = document.createElement('div');
item.className = 'comment';
const body = document.createElement('p');
body.textContent = comment.body; // escaped by browser
item.appendChild(body);
document.getElementById('comments').appendChild(item);
}
Testez vos défenses
Après la mise en œuvre de la prévention, testez votre application à l'aide de scanners automatisés et de charges utiles manuelles.
Utilisez les outils de développeur de navigateur pour examiner le DOM et s'assurer que les charges utiles sont échappées.
Conclusion
En validant les entrées, en surveillant les changements DOM, en s'échappant de la sortie et en s'intégrant à des bibliothèques robustes comme DOMPurify, vous pouvez durcir considérablement la sécurité de votre client. N'oubliez pas que les mesures côté client ne sont pas une puce argentée; elles complètent une stratégie de défense en profondeur qui inclut la désinfection côté serveur, les en-têtes CSP et les audits de sécurité réguliers.
Pour plus de renseignements, veuillez consulter la page du SRS de l'OWASP et la feuille de chaleur de prévention du SRS de l'OWASP.