Failles XSS (Cross-site Scripting) : principes, types d'attaques, exploitations et bonnes pratiques sécurité

Le Cross-site Scripting (abrégé XSS) est une vulnérabilité particulièrement répandue dans les applications web. En effet, plus d’une application sur deux en contiendrait selon diverses études, d’anciennes comme de plus récentes. Pour étayer ce propos, il s’agit de la vulnérabilité la plus courante que nous découvrons et exploitons lors de nos pentests sur tous types d’applications et de sites web.   

Principes, types d’attaques XSS, exploitations, nous vous présentons dans cet article une vue d’ensemble des XSS, ainsi que les bonnes pratiques sécurité et mesures à implémenter pour contrer les risques d’attaques. 

Qu’est-ce qu’une faille XSS ?

Les XSS font partie de la catégorie des vulnérabilités par injection de code au même titre que les injections SQL. Cependant pour découvrir et exploiter une faille XSS, il s’agit pour un attaquant d’injecter du code malveillant via les paramètres d’entrée côté client.

L’objectif : détourner la logique d’une application web et permettre par exemple le vol de cookies ou de jetons de session, l’altération de données ou de contenus, l’exécution d’un malware, etc. Les possibilités d’exploitation d’une faille XSS sont nombreuses, presque infinies avec des conséquences souvent critiques voire irréversibles. Retenez juste qu’un attaquant peut potentiellement réaliser toute action via le navigateur web d’une victime.

Comment identifier et exploiter une faille XSS ? 

Les XSS sont dues à des failles de sécurité dans le code d’une application web. En effet, lorsqu’une application permet d’exécuter du code malveillant de la même manière que le code Javascript légitime, des attaques XSS sont possibles. Voyons cela plus en détail, étape par étape.

Dans un premier temps, il s’agit d’identifier les différents champs de saisie disponibles sur une application web. Prenons par exemple un formulaire de contact dans lequel un utilisateur peut saisir différentes informations.

Si on écrit « Vaadata » dans l’input on voit qu’il est ajouté dans le code et affiché par la console, ce qui indique que l’application web peut être vulnérable à une attaque XSS :

Pour confirmer notre intuition, entrons maintenant un guillemet double dans l’input et, dans notre cas, on obtient un nombre impair de guillemets dans le code Javascript ce qui crée une erreur de syntaxe, erreur que l’on peut retrouver dans la sortie de la console.

Cela confirme que l’on peut ainsi manipuler le code Javascript et injecter du code malveillant qui sera exécuté par le navigateur.

Ici, par exemple, on remplit la fonction « console.log() » avec la valeur « Vaadata », puis on referme les guillemets double de la fonction. Enfin on peut ajouter les « // » afin de commenter tout le reste de la ligne, afin que le code qui suit ne soit pas exécuté. On obtient ainsi le mot « Vaadata » dans la sortie de la console.

On peut ajouter notre payload Javascript (ici : <script>alert(1)</script>) afin que la payload finale devienne :

<script>console.log("Vaadata");</script><script>alert(1)</script>//");</script> 

Ou plus simplement :

<script>console.log("Vaadata");</script><script>alert(1)</script>

On observe que l’attaque XSS réalisée ici est un succès. Ainsi, avec un script personnalisé – en fonction de ses objectifs – un attaquant pourrait voler le jeton de session ou les informations du cookie d’un utilisateur et ainsi accéder au compte de ce dernier pour lancer différents types d’attaques.

Quels sont les types d’attaques XSS ?

Il existe trois types d’attaques XSS : les attaques XSS stockées (stored XSS), les attaques XSS reflétées (reflected XSS) et les attaques XSS basées sur le DOM (DOM-based XSS). Toutes consistent en l’utilisation de scripts malveillants entrés côté client et qui seront inclus et interprétés sur le navigateur d’un utilisateur.

Attaques XSS stockées (stored XSS)

Comme son nom l’indique, lors d’une telle attaque, le script malveillant sera stocké sur le serveur. Ce qui, avouons-le, peut avoir des conséquences fâcheuses car tout utilisateur qui visite la page contenant le script injecté peut être infecté, donc affecté par l’attaque XSS via son navigateur.

De fait, il s’agit de l’attaque XSS la plus dangereuse car l’attaquant n’injecte le code malveillant dans le serveur qu’une seule fois et peut toucher un grand nombre d’utilisateurs, parmi lesquels des administrateurs. Nous vous laissons imaginer toutes les exploitations et conséquences possibles.

Attaque XSS stockée

Attaques XSS reflétées (reflected XSS)

Lors d’une attaque XSS reflétée réussie, le script malveillant est renvoyé dans la réponse du serveur. Dans ce cas de figure, un attaquant transmet à un utilisateur du code via une URL. Un système de chat, un email de phishing ou un message privé sur les réseaux sociaux peuvent ici servir de relai pour l’attaque afin de forcer un clic sur le lien. Et si clic il y a, une requête contenant un script malveillant sera envoyée au serveur puis le code sera renvoyé en réponse à l’utilisateur car il ne sera pas stocké.

Attaque XSS reflétée

Attaques XSS basées sur le DOM (DOM-based XSS)

Les DOM-based XSS sont également des attaques exécutées côté client, à la seule différence (des deux premières mentionnés ci-dessus) qu’elles n’utilisent pas le serveur. En effet, les attaques DOM XSS exploitent uniquement le navigateur de la victime car le DOM (Document Object Model) est une interface permettant de traiter et modifier le contenu d’une page web.

Dans ce cas de figure donc, si possibilité d’injection il y a, le script malveillant injecté permettra de modifier la structure du DOM et ainsi permettre un vol de données par exemple.

La plupart du temps, les propriétés DOM telles que document.location, document.write et document.anchors sont utilisées pour lancer ce type d’attaque XSS. Cependant, cette vulnérabilité est plutôt rare car il est très difficile de l’identifier. Pour en savoir plus, nous vous renvoyons vers notre article dédié : DOM-based XSS, principes, impacts, exploitations et bonnes pratiques sécurité.

Comment se protéger des failles XSS ?

Pour prévenir les attaques XSS, il faut partir du principe que les données reçues par une application web ne peuvent pas être considérées comme « toujours » sûres.

Il est donc important d’implémenter des mesures de protection pour traiter toutes les données venant de l’extérieur. Ainsi, tout contenu doit être filtré, validé et encodé avant d’être utilisé par l’application.

Encoder (échapper) les données en entrée et en sortie

L’encodage (ou échappement) des données en entrée ou en sortie reste la mesure de sécurité essentielle pour prévenir les attaques XSS. L’objectif rappelons-le est d’empêcher l’exécution d’un code malveillant dans le navigateur des utilisateurs. Pour ce faire, il s’agit de remplacer les caractères spéciaux par des valeurs encodées, de manière à ce que toute donnée saisie par un utilisateur soit traitée (reçue et interprétée) comme du texte, plutôt que comme du code.

Ainsi, tout script malveillant injecté par un attaquant ne sera pas exécuté par le navigateur – stocké sur le serveur ou reflété – si l’encodage est appliqué correctement. Par conséquent, aucun utilisateur ne sera affecté par une attaque XSS potentielle.

Il existe de nombreux types d’encodage qui peuvent être appliqués – certains plus importants que d’autres – en fonction du type d’application (fonctionnalités critiques, logique métier, données traitées et stockées, etc.). Retenez juste que les paramètres doivent être systématiquement encodés pour contrer les risques d’attaques XSS. Maintenant voyons, la principale méthode d’encodage (l’encodage d’entités HTML) plus en détail :

L’encodage d’entités HTML est sûrement la mesure la plus essentielle car les scripts malveillants sont souvent injectés via des balises HTML. Ici, il s’agit donc d’encoder la plupart des entités HTML pouvant constituer un risque afin qu’elles soient interprétées comme des données fiables. Par exemple, l’entité « < » , normalement interprété comme le début d’une balise HTML pourrait être encodé en « &lt » de manière à ce qu’elle soit reçue et traitée par l’application comme telle. De la même manière, il est possible (et fortement recommandé) d’encoder les valeurs d’attributs qui peuvent être utilisées pour injecter des scripts malveillants comme : href, src, style, etc.

Filtrer les données reçues côté client

Une autre mesure de prévention contre une attaque XSS est le filtrage des données reçues côté client. Cela signifie que l’ensemble des données doit passer par un filtre qui élimine les caractères dangereux comme la balise <script>, les gestionnaires d’événements HTML comme onActivate(), onClick(), les éléments JavaScript, etc.

Comme pour l’encodage, les données peuvent être filtrées en entrée et/ou en sortie. Filtrer en entrée consiste, comme évoqué plus haut, à supprimer des mots clés potentiellement dangereux des entrées utilisateurs, tandis que le filtrage en sortie est appliqué aux données qui sont renvoyées dans la page web de réponse. Cette méthode fonctionne essentiellement pour les attaques XSS stockées.

Valider les entrées utilisateurs 

Valider les entrées utilisateurs est également une mesure de sécurité pour contrer les attaques XSS. L’idée ici est de faire en sorte que tous les champs de saisie correspondent au type de données attendu. Par exemple, pour un champ de saisie d’un numéro de téléphone, il doit être impossible pour un utilisateur d’insérer du texte. De la même manière, les balises HTML n’étant pas légitimes dans ce type de formulaire, elles doivent être validées pour empêcher les attaquants de soumettre des scripts malveillants.  

Utiliser le standard CSP (Content Security Policy)

Pour exploiter une faille XSS, un attaquant peut contourner le navigateur d’un utilisateur et injecter un script malveillant à partir de sources externes, de manière à ce que le navigateur ne soit pas capable de distinguer les scripts malveillants des légitimes.

Pour réduire le risque de ce type d’exploitation XSS (et tout type d’injection de code), Mozilla a développé un mécanisme de sécurité connu sous le nom de Content Security Policy (CSP). Ce standard de sécurité permet de contrôler (restreindre via autorisation) les sources externes (autres sites web) de récupération de données. 

Ainsi, si ce mécanisme est en place, le navigateur sera autorisé à accéder uniquement aux ressources figurant sur la liste blanche, en ignorant tous les autres domaines. Les scripts injectés ne seront donc pas exécutés même si un attaquant découvre des possibilités d’injection XSS.