Prévenir les attaques XSS dans Django
13 mars 2023
0 minutes de lectureLe cross-site scripting (XSS) est un type de vulnérabilité qui consiste à manipuler les interactions d’un utilisateur avec une application Web afin de compromettre son environnement de navigation. Ces vulnérabilités peuvent toucher de nombreuses applications Web, y compris celles créées avec des frameworks modernes comme Django.
Étant donné que les attaques XSS sont très répandues, il est essentiel de protéger vos applications contre elles. Ce guide explique comment les vulnérabilités XSS apparaissent dans les applications Django et comment les atténuer. Vous découvrirez également comment utiliser des outils de sécurité gratuits pour détecter et corriger les vulnérabilités XSS dès le début du développement.
Qu’est-ce que le XSS ?
Le terme XSS remonte aux débuts de ces attaques, lorsque le vol de données intersites était la principale préoccupation des attaquants. Mais les attaques XSS ont évolué et désignent aujourd’hui toute attaque permettant de compromettre les données, les jetons, etc. côté client. Une attaque réussie peut entraîner aussi bien le détournement d’une session que la prise de contrôle complète d’un compte ou d’un système.
Lors d’une attaque XSS, des données indésirables pénètrent dans une application et sont renvoyées à un utilisateur sans validation. Les données malveillantes incluses dans la réponse prennent souvent la forme de JavaScript ou d’un autre code exécutable dans le navigateur de l’utilisateur. Lorsque le navigateur exécute ce code, il contourne sa politique de même origine (SOP) et exploite l’utilisateur.
Historiquement, les attaques XSS étaient classées en deux catégories selon la persistance des données : réfléchies et stockées. Il existe également un type d’attaque XSS plus récent, appelé XSS basé sur le DOM.
XSS stocké
Les attaques XSS stockées, parfois appelées attaques XSS persistantes, se produisent lorsque du code malveillant est stocké sur le serveur d’une application vulnérable, puis envoyé aux utilisateurs sans assainissement (et finalement exécuté par leur navigateur). La charge utile malveillante est souvent stockée dans des bases de données, des publications de forum, des journaux ou des champs de commentaire.

Lorsqu’une victime visite l’application concernée, la charge utile malveillante est transmise à son navigateur dans la réponse du serveur. Le navigateur exécute alors ces données et l’utilisateur est compromis.
XSS réfléchi
Les attaques XSS réfléchies se produisent lorsque les entrées utilisateur renvoyées par l’application ne sont pas enregistrées par le serveur. Dans ce cas, le code non fiable est envoyé dans un résultat de recherche ou un message d’erreur sans être correctement assaini.

L’attaquant envoie généralement la charge utile à la victime sous un faux prétexte. Lorsque la victime clique dessus, le code malveillant est transmis à l’application concernée. Celle-ci renvoie le code malveillant, qui s’exécute dans le navigateur de la victime.
XSS basé sur le DOM
Les attaques XSS basées sur le DOM se produisent lorsque le flux de données malveillantes commence et se termine dans le DOM du navigateur. La vulnérabilité est déclenchée par la manipulation du DOM du navigateur de la victime ; la réponse HTTP elle-même ne change pas, mais renvoie simplement le code côté client, qui modifie ensuite le DOM du navigateur de manière à exécuter la charge utile.
Prévenir les attaques XSS dans Django
Django intègre des protections contre les attaques XSS, notamment un mécanisme d’échappement automatique pour son moteur de templates. Cependant, même s’il peut bloquer les attaques XSS courantes, il ne protège pas contre les vecteurs d’attaque résultant de mauvaises pratiques de codage.
Vous pouvez réduire les risques d’attaques XSS en suivant les bonnes pratiques lors du développement de votre application.
Encadrez les données dynamiques
Toute partie de votre application qui accepte des données utilisateur sans les assainir peut introduire des vulnérabilités XSS. Les attaquants peuvent utiliser ces champs de saisie pour injecter du code JavaScript arbitraire qui ne nécessite pas de caractères HTML.
Par exemple, le code HTML suivant utilise un attribut sans guillemets, qui peut être modifié pour contenir des gestionnaires JavaScript, comme onmouseover=alert(1) :
Bien entendu, les véritables attaquants ne se montreront pas aussi inoffensifs. Ils injecteront plutôt des charges utiles personnalisées pour tirer le meilleur parti de cette possibilité. Les variables JavaScript utilisées pour stocker des données utilisateur constituent un autre exemple de vulnérabilité XSS liée aux charges utiles non encadrées par des guillemets :
Utiliser des entiers JavaScript pour stocker une valeur fournie de cette façon expose votre application aux attaques XSS. Le mécanisme d’échappement automatique de Django ne vous en protège pas. Un attaquant peut saisir une valeur aussi simple que 23 ; payload() et exécuter du code malveillant dans le navigateur d’un utilisateur.
Encadrer tous les attributs destinés à l’utilisateur par des guillemets doubles permet d’atténuer ces vulnérabilités :
Évitez les littéraux de template
Si votre code contient des littéraux de template JavaScript, il peut être vulnérable aux attaques XSS. Les littéraux de template utilisent des accents graves au lieu de guillemets et n’échappent pas les caractères suivants :
Les attaquants peuvent ainsi exécuter du code en créant des charges utiles avec des accents graves. La meilleure façon d’éviter ce problème est d’interdire l’utilisation des littéraux de template.
Validez les URL des attributs
De nombreux attributs HTML, comme a href et img src, attendent une URL et peuvent être exploités lors d’attaques XSS. Les attaquants peuvent simplement intégrer des charges utiles utilisant un URI javascript: pour exécuter leur code. L’échappement HTML de Django ne protège pas contre ce cas.
Par exemple, imaginons que votre application permette aux utilisateurs d’ajouter l’URL de leur site Web personnel. Des utilisateurs malveillants peuvent utiliser une valeur comme javascript:payload(XSS) comme lien. Dès que quelqu’un clique sur ce lien, la charge utile malveillante s’exécute.
Pour résoudre ce problème, vous pouvez créer une liste d’autorisation des protocoles acceptés pour les URL et bloquer tous les autres. Parmi les URI sûrs figurent http:, https:, ftp: et mailto:. Vous pouvez également assainir les URL des attributs avant de les enregistrer dans la base de données.
Échappez les données JavaScript
Insérer directement des données variables dans JavaScript les place dans le contexte d’exécution. Si votre programme permet aux utilisateurs de contrôler les données insérées dans des blocs JavaScript ou des gestionnaires d’événements, cela peut entraîner des attaques XSS.
Comme les attaquants peuvent créer du code sans caractères HTML, se fier à l’échappement HTML de Django ne suffit pas à prévenir les attaques XSS dans ces cas.
Pour atténuer ce risque, vous pouvez interdire les variables de template dans les blocs <script>, ou utiliser la balise de template json_script pour lire des données JS :
N’oubliez pas d’encoder les caractères des données au format \xHH et de vérifier que l’en-tête Content-Type du JSON est application/json, et non text/html.
Échappez les données CSS
Les données insérées dans des balises ou des attributs de style CSS peuvent introduire des vulnérabilités XSS dans votre application Django. Les attaquants peuvent exploiter ces données CSS pour accéder au contexte d’exécution. Pour ce faire, ils injectent généralement du code JavaScript dans des contextes CSS :
La directive expression() permet d’utiliser des instructions JavaScript arbitraires pour évaluer la valeur d’une propriété CSS, ce qui expose votre application Django aux attaques XSS.
Pour atténuer ce risque, évitez de définir les valeurs de vos paramètres CSS à l’aide de la directive expression(). Lorsque vous utilisez des instructions JavaScript pour définir ou modifier des propriétés CSS, essayez plutôt quelque chose comme style.property = x.
Utilisez le filtre safe avec parcimonie
L’échappement automatique de Django fonctionne bien dans la plupart des cas, mais il ne peut pas vous aider si vous désactivez l’échappement HTML avec le filtre safe dans un template. Lorsque vous marquez un élément comme safe, Django ne l’échappe pas et affiche les données telles quelles.
Si votre application affiche cette requête sans valider les données, des attaquants peuvent l’utiliser pour transmettre des charges utiles. N’utilisez le filtre safe que si vous avez la certitude que les données sont réellement sûres ; les données fournies par les utilisateurs ne doivent jamais être marquées comme safe.
Limitez l’utilisation du filtre safeseq
Le filtre safeseq indique que le contenu à afficher est parfaitement sûr. Il peut introduire des charges utiles XSS, tout comme le filtre safe.
Évitez d’utiliser ce filtre pour les données de séquence. Si vous devez le faire, utilisez mark_safe(). Vous pourrez ainsi repérer, lors de la revue de code, les occurrences où vous avez explicitement marqué un élément comme sûr.
Limitez l’utilisation de html_safe()
La méthode html_safe() ajoute la méthode magique __html__ à la classe fournie, qui renvoie une représentation sous forme de chaîne exacte de cette classe :
Ces données ne sont pas échappées par le moteur de templates de Django et constituent donc un risque potentiel d’attaque XSS. N’utilisez html_safe() que si nécessaire. Évitez également d’utiliser la méthode magique __html__ dans une classe.
Évitez les filtres avec is_safe=True
Si vous enregistrez un filtre personnalisé avec is_safe=True, Django n’échappe pas le HTML et marque comme sûre la valeur renvoyée par le filtre.
Comme les données renvoyées par ce filtre ne passent pas par l’échappement automatique de Django, elles peuvent entraîner des attaques XSS. Pour contribuer à la sécurité de votre application, évitez d’enregistrer des filtres de cette façon, sauf si vous êtes certain que les données renvoyées sont sûres.
Limitez l’utilisation de mark_safe() et SafeString
La méthode mark_safe() marque les données affichées comme sûres et contourne les protections XSS intégrées de Django. Son utilisation fréquente peut favoriser l’apparition de vulnérabilités XSS.
De plus, lorsque vous utilisez mark_safe(), la méthode renvoie les données sous forme de SafeString. Django utilise la classe SafeString pour déterminer quelles données peuvent être affichées sans risque. Que se passe-t-il donc lorsque vous utilisez directement SafeString ?
Comme vous l’avez sans doute deviné, utiliser directement la classe SafeString de cette manière contourne l’échappement HTML de Django et peut entraîner des attaques XSS. Il est préférable de limiter l’utilisation de mark_safe() et de SafeString.
Évitez de générer des réponses avec HttpResponse
L’utilisation directe de HttpResponse ou de classes similaires dans votre code contourne le système de templates de Django et désactive ainsi l’échappement HTML.
Cela peut exposer votre programme aux attaques XSS : évitez donc de générer vos réponses de cette façon. Utilisez plutôt la méthode render() avec un template.
Détectez et corrigez les vulnérabilités XSS dans Django avec Snyk
Les attaques XSS figurent parmi les menaces les plus actives contre les applications Web modernes. Comme elles peuvent se produire de nombreuses façons, il est difficile de détecter toutes les vulnérabilités XSS lors d’une revue de code. Il est donc essentiel de repérer ces vulnérabilités dès les premières étapes du cycle de développement logiciel (SDLC).
Pour ce faire, vous pouvez intégrer des outils de test dédiés à votre environnement de développement. Nous allons vous montrer comment avec Snyk.
Snyk propose plusieurs façons de tester les applications Django : interface Web, interface de ligne de commande, plug-ins IDE et API. Dans ce tutoriel, vous utiliserez le plug-in Snyk pour PyCharm. Vous devrez avoir installé PyCharm pour suivre les étapes (Snyk prend également en charge d’autres IDE, comme Eclipse et Visual Studio).
Pour utiliser Snyk, créez un compte gratuit, puis installez le plug-in en suivant le guide d’installation.

Après avoir installé le plug-in, configurez le plug-in Snyk pour votre IDE. Vous devez le connecter à la plateforme Snyk, ce qui nécessite la CLI Snyk. Toutefois, vous n’avez pas besoin de l’installer séparément : lorsque vous lancerez une analyse pour la première fois, Snyk la téléchargera automatiquement.

Vous serez invité à vous authentifier auprès de Snyk. Cliquez sur Tester le code maintenant pour accéder à l’interface Web de Snyk.

Cliquez sur S’authentifier pour confirmer l’intégration de votre plug-in.

Une fois l’authentification terminée, Snyk commence automatiquement à analyser votre application Django et affiche les problèmes sous l’onglet Snyk de votre IDE. Vous pouvez également lancer une analyse en cliquant sur Lancer l’analyse.

Snyk affiche des informations utiles sur les vulnérabilités détectées, notamment leur niveau de gravité et les correctifs suggérés.

Vous pouvez corriger les vulnérabilités en appliquant les mises à niveau recommandées par Snyk. Le plug-in recommande toujours les modifications minimales nécessaires pour corriger votre code.
Conclusion
Les attaques XSS figurent parmi les problèmes de sécurité les plus courants dans les applications Django. Comme elles peuvent provenir de nombreuses sources et prendre différentes formes, il est difficile de les prévenir complètement. Vous pouvez toutefois réduire les risques d’attaques XSS en suivant ces bonnes pratiques.
Utiliser les bons outils pendant le développement est également essentiel. Une plateforme de sécurité dédiée comme Snyk peut vous aider à détecter et à corriger les vulnérabilités XSS dès les premières étapes du développement de votre application.
Lancez-vous dans les compétitions Capture The Flag
Apprenez à résoudre des défis Capture The Flag en regardant à la demande notre atelier virtuel d’initiation.
