Empêcher les attaques cross-site scripting (XSS) dans les applications Java avec Snyk Code
25 avril 2023
0 minutes de lectureJava est un langage de programmation puissant qui permet aussi d’écrire des pages HTML pour des applications Web. Néanmoins, la création de telles pages peut exposer à des risques de sécurité associés aux attaques de type cross-site scripting (XSS). Avec l’essor des frameworks de templating modernes, il est devenu plus facile de prévenir les attaques de sécurité grâce à des techniques appropriées de validation et d’encodage des saisies. Lorsque les développeurs choisissent de créer leurs propres pages HTML sans utiliser de tel framework, ils courent un risque accru d’introduire des vulnérabilités.
Par exemple, le fait d’utiliser l’objet HttpServletResponse
dans une application Spring MVC pour écrire du contenu directement dans la réponse peut permettre à des utilisateurs malveillants d’injecter du code dans la page, ouvrant ainsi la voie à de potentielles attaques XSS. Il est donc essentiel que les développeurs garantissent la sécurité de leurs applications Web Java en implémentant des mesures appropriées pour prévenir les vulnérabilités XSS lors du codage de leurs pages HTML.
La solution ci-dessous, très simple, permet d’implémenter une page avec rendu côté serveur sans avoir recours à un framework sophistiqué, qui est assorti habituellement d’instructions spécifiques. Bien entendu, elle présente malgré tout des inconvénients.
Écrire une sortie HTML avec Spring MVC, sans framework de templating
Supposons que votre application Web récupère le nom d’un produit et l’affiche sur une page à l’aide de l’objet HttpServletResponse
. Avec un contrôleur Spring MVC, vous pourriez procéder comme suit :
1@GetMapping("/direct")
2public void directLink (@RequestParam String param, HttpServletResponse response) throws IOException {
3 Product prod = productService.getProductByName(param);
4
5 response.setContentType("text/html");
6 var writer = response.getWriter();
7 writer.write(head);
8 writer.write("<div class=\"panel-heading\"><h1>"+ param + "</h1></div>");
9
10 String output = "<div class=\"panel-body\">" +
11 "<ul>" +
12 "<li>%s</li>" +
13 "<li>%s</li>" +
14 "<li>%s</li>" +
15 "</ul>" +
16 "</div>";
17
18 writer.write(String.format(output, prod.getDescription(), prod.getProductType(), prod.getPrice()));
19 writer.write(foot);
20
21 response.getWriter().flush();
22}
Êtes-vous capable de déterminer quelles sortes de vulnérabilités de sécurité pourraient être introduites par l’intermédiaire du code Java ci-dessus ?
Repérer les failles XSS avec Snyk Code
Regardez de plus près la fonction ci-dessus. Peut-être avez-vous déjà identifié au moins une vulnérabilité XSS, voire deux. Après analyse, Snyk Code me signale deux problèmes XSS différents dans cette méthode.
Il existe de multiples façons de tirer profit de Snyk Code. Nous allons en voir trois. Pour les développeurs, la manière la plus directe d’obtenir des informations de Snyk Code consiste à installer un plugin dans l’IDE. Nous proposons des plugins pour de nombreux IDE différents. Dans l’exemple suivant, je montre comment le plugin IntelliJ m’aide à détecter les problèmes XSS au cours du développement.
Sortie du plugin IntelliJ :
Une autre possibilité consiste à exécuter Snyk Code à l’aide de Snyk CLI. En exécutant la commande snyk code test
à partir du terminal, vous obtiendrez un résultat similaire au suivant. Cette méthode s’avère utile sur votre machine locale ou dans le cadre d’un processus de build automatisé au sein d’un pipeline CI/CD.
Sortie de la CLI :
La troisième possibilité que je souhaite vous présenter repose sur l’interface Web. Pour cette sortie, j’ai utilisé l’intégration git avec Snyk et j’ai connecté mon dépôt GitHub à l’interface Web de Snyk à l’aide du tableau de bord disponible sur https://app.snyk.io. Cette solution recherche les vulnérabilités de sécurité dans le code qui est transféré vers mon dépôt.
Sortie de l’interface Web :
Ces trois options d’analyse me montrent qu’il y a deux problèmes de sécurité XSS distincts à résoudre. Snyk Code indique leur emplacement exact dans mon code. Passons-les au crible et voyons comment nous pouvons y remédier.
Attaques XSS par réflexion
Une attaque XSS par réflexion est un type d’attaque XSS qui se produit lorsqu’un utilisateur injecte un code malveillant dans une application Web, code qui est ensuite renvoyé à d’autres utilisateurs par le biais d’une réponse. Dans l’exemple que j’ai donné, si la saisie de l’utilisateur n’était pas correctement validée ou nettoyée avant d’être incluse dans la réponse, un utilisateur malveillant pourrait injecter un script qui serait exécuté par d’autres utilisateurs consultant la page Web. Ce type d’attaque XSS sert souvent à subtiliser des données utilisateur, modifier le contenu d’un site Web ou effectuer d’autres actions malveillantes.
Le code ci-dessus récupère le nom de l’utilisateur à partir du paramètre de la requête HTTP, avant de l’inscrire directement dans l’objet HttpServletResponse :
1writer.write("<div class=\"panel-heading\"><h1>"+ param + "</h1></div>")
Ce code est vulnérable aux attaques XSS parce qu’il ne valide/nettoie pas correctement la saisie de l’utilisateur. Un utilisateur malveillant pourrait par exemple injecter du code HTML ou JavaScript dans le paramètre « name », qui serait ensuite exécuté par d’autres utilisateurs consultant la page Web.
Exemple : .../direct?param=
<script>
alert(document.cookie);
</script>
pourrait révéler des informations personnelles stockées dans vos cookies. Cela signifie qu’il est possible d’envoyer ces informations à un autre serveur sans que vous le sachiez.
Snyk Code m’a permis d’identifier cette erreur en signalant la faille XSS à la ligne 93.
Attaques XSS stockées
L’attaque XSS stockée, en revanche, est un type d’attaque XSS dans lequel le code malveillant est stocké sur le serveur, puis transmis à tous les utilisateurs qui accèdent à la page concernée. Dans l’exemple que j’ai donné, si la saisie de l’utilisateur n’était pas correctement validée ou nettoyée et qu’elle était stockée dans une base de données, un utilisateur malveillant pourrait injecter un script qui serait transmis à tous les utilisateurs consultant la page touchée. Ce type d’attaque XSS est particulièrement dangereux, car il est susceptible d’affecter un grand nombre d’utilisateurs et de persister même après la correction de l’injection initiale.
Le code ci-dessus récupère un produit à partir de ProductService
, avant de l’afficher dans les champs en tant que partie de la chaîne de sortie. Ce code est toutefois vulnérable aux attaques XSS stockées, car il omet de valider ou de nettoyer correctement l’entrée provenant de la base de données. La phase de nettoyage est particulièrement importante si vous n’êtes pas sûr de connaître l’identité des personnes autorisées à écrire dans la base de données. Un utilisateur malveillant pourrait par exemple soumettre une description de produit comprenant du code HTML ou JavaScript, qui serait stocké dans la base de données et transmis à tous les utilisateurs consultant la vue du produit.
Snyk Code a justement mis en évidence ce problème XSS potentiel à la ligne 103, où nous insérons le champ product.description
dans la chaîne de sortie sans le valider ou le nettoyer.
Résoudre les vulnérabilités XSS avec Snyk Code
Pour éviter les vulnérabilités XSS, il est important de valider et de nettoyer correctement les informations saisies par l’utilisateur avant de les inscrire dans la réponse. Snyk Code nous aide déjà en nous suggérant les solutions envisageables. Une façon d’y parvenir consiste à utiliser une bibliothèque comme Apache Commons Text pour encoder la saisie et empêcher l’exécution de code malveillant.
La fonction escapeHtml4()
permet d’échapper le code susceptible d’être utilisé lors d’attaques XSS par réflexion et stockées afin d’éviter son exécution lors du chargement de la page.
D’autres bibliothèques peuvent évidemment procéder au même type d’échappement. Outre Apache Commons Text, vous pouvez envisager OWASP Encoder. Si vous travaillez avec Spring, vous pouvez également utiliser HtmlUtils.htmlEscape.
Si vous utilisez Apache Commons Text, le code échappé ressemblera probablement à ceci :
1@GetMapping("/direct")
2public void directLink (@RequestParam String param, HttpServletResponse response) throws IOException {
3 Product prod = productService.getProductByName(param);
4
5 response.setContentType("text/html");
6 var writer = response.getWriter();
7 writer.write(head);
8 writer.write("<div class=\"panel-heading\"><h1>"+ StringEscapeUtils.escapeHtml4(param) + "</h1></div>");
9
10 String output = "<div class=\"panel-body\">" +
11 "<ul>" +
12 "<li>%s</li>" +
13 "<li>%s</li>" +
14 "<li>%s</li>" +
15 "</ul>" +
16 "</div>";
17
18 writer.write(String.format(output,
19 StringEscapeUtils.escapeHtml4(prod.getDescription()),
20 StringEscapeUtils.escapeHtml4(prod.getProductType()),
21 StringEscapeUtils.escapeHtml4(prod.getPrice())));
22
23 writer.write(foot);
Attention aux frameworks de templating
Les frameworks de templating comme Thymeleaf peuvent contribuer à la protection contre les vulnérabilités XSS. Thymeleaf est un moteur de templating populaire pour Java qui intègre la prise en charge de l’échappement du code HTML, un moyen de prévenir les attaques XSS en encodant toute saisie utilisateur incluse dans le rendu HTML.
Néanmoins, cette protection dépend de la manière dont vous créez le modèle. Voici une façon d’utiliser Thymeleaf pour le rendu d’un produit similaire à l’exemple précédent :
1<div class="product" th:each="product : ${products}">
2 <p th:text="${product.name}"></p>
3 <p th:text="${product.type}"></p>
4 <p th:text="${product.pric}"></p>
5 <p th:utext="${product.descriptiont}"></p>
6</div>
Dans cet exemple, les attributs th:text
seront échappés, mais pas l’attribut th:utext
. Cet attribut th:utext
restitue le texte du commentaire sans échappement des balises HTML ou des caractères spéciaux, et présente donc une vulnérabilité potentielle aux attaques XSS. Si vous utilisez un framework, il est donc essentiel de bien comprendre le comportement des différents éléments.
Repérer les failles XSS avant le déploiement en production
La prévention des attaques XSS constitue une préoccupation majeure des développeurs qui travaillent sur des applications Web Java. Il est essentiel d’identifier et de traiter les vulnérabilités XSS le plus tôt possible lors du processus de développement. Bien que le nettoyage des saisies utilisateur puisse limiter efficacement le risque d’attaque XSS, il n’est pas toujours suffisant.
Consultez des ressources telles que le mémo OWASP XSS et la leçon Snyk Learn sur les attaques XSS pour vous tenir au courant des dernières menaces et des meilleures pratiques en matière de prévention XSS.
Il est également important d’utiliser les outils appropriés pour repérer les erreurs XSS et autres problèmes de sécurité avant qu’ils ne rejoignent le code en production. Snyk Code est un outil gratuit précieux permettant d’identifier les vulnérabilités de sécurité potentielles dès le début du cycle de développement. En adoptant une approche proactive de la prévention des attaques XSS et en faisant appel aux ressources et aux outils appropriés, les développeurs peuvent assurer la sécurité et l’intégrité de leurs applications Web Java.
Cap sur la capture du drapeau
Découvrez comment résoudre les défis de capture du drapeau en regardant notre atelier virtuel à la demande.