Durant nos audits il nous arrive encore très souvent de rencontrer des énumérations utilisateurs qui pourraient avec les bonnes méthodes être facilement évitées. Dans cet article, nous traiterons des énumérations d’utilisateurs sur les formulaires de connexion, de réinitialisation de mot de passe et de création de compte. Cependant, les énumérations utilisateur peuvent se trouver sur d’autres fonctionnalités, telles que, par exemple, des formulaires de recherches ou des envois de messages.
Description
Définition
Une énumération d’utilisateur permet comme son nom l’indique de trouver des identifiants de connexion utilisateurs valides sur une application. Pour ce faire, un attaquant tentera d’entrer un certain nombre de noms d’utilisateurs et observera le comportement de l’application pour déterminer si un identifiant est valide ou non (message d’erreur différent, temps de réponse différents et plus généralement toute différence au niveau réponse HTTP).
Pour constituer une liste de candidats, un attaquant pourra par exemple essayer de trouver des adresses email ou des personnes liées à sa cible sur des moteurs de recherche ou sur les réseaux sociaux.
Pourquoi cela pose problème
Cela pose problème tout d’abord parce que si un attaquant trouve un identifiant utilisateur valide, il pourra l’utiliser pour affiner ses attaques. Il pourra par exemple tenter une attaque par bruteforce sur le formulaire de connexion de la plateforme si celui-ci est vulnérable à ce type d’attaque, ou bien encore cibler plus spécifiquement les personnes identifiées avec des attaques par phishing.
Qui est concerné
Il est vrai que sur les formulaires de connexion cette vulnérabilité est assez rare à l’heure actuelle. Cependant, elle reste assez répandue sur les formulaires de réinitialisation de mots de passe et encore plus sur les formulaires de création de compte.
Afin d’effectuer cette démonstration, nous avons développé une application vulnérable sous PHP qui contient un formulaire de connexion, un formulaire de réinitialisation de mot de passe ainsi qu’un formulaire de création de compte.
Formulaire de connexion vulnérable
Comme mentionné plus haut, cette vulnérabilité reste assez rare sur les formulaires de connexion, cependant il nous arrive encore de l’observer.
Sur la capture d’écran ci-dessus, nous constatons que l’application nous informe que le nom d’utilisateur n’existe pas. Si nous tapons un nom d’utilisateur qui existe, le message d’erreur est différent.
Nous pouvons donc en déduire que le nom d’utilisateur « admin » existe bien sur la plateforme. Il ne reste plus qu’à trouver le mot de passe.
Afin de corriger cette vulnérabilité sur un formulaire de connexion, il faut simplement faire en sorte que le message d’erreur renvoyé par l’application soit le même, quelle que soit la source du problème (mot de passe ou nom d’utilisateur).
De cette manière, il n’est plus possible de procéder à une énumération de comptes utilisateurs.
Réinitialisation du mot de passe
Les énumérations d’utilisateur sont beaucoup plus présentes sur les formulaires de réinitialisation de mot de passe. En effet, il arrive souvent que l’application informe l’utilisateur que l’adresse email ou le nom d’utilisateur qu’il a entré dans le champ n’existe pas sur la plateforme.
Si l’adresse email existe bien, un autre message est affiché indiquant que le lien de réinitialisation du mot de passe a été envoyé.
Afin de corriger ce problème, il est possible d’afficher systématiquement le même message, que l’utilisateur existe ou non, par exemple : « Si votre compte existe, un lien de réinitialisation de mot de passe vous sera envoyé dans quelques instants. ».
Le message d’erreur renvoyé à l’utilisateur par l’application est désormais le même que le compte existe ou non.
Cependant, il reste souvent un problème. Si l’application envoie l’email de réinitialisation directement lorsque l’utilisateur valide son formulaire, un temps de latence plus grand sera observé si l’adresse email existe (si elle n’existe pas, cet envoi n’aura pas lieu).
Temps de réponse avec un compte inexistant (en secondes) :
curl -i -s -k -w %{time_total}\\n -o /dev/null -X $’POST’ -H $’Content-Type: application/x-www-form-urlencoded’ –data-binary $’email=test%40test.com&button=’ $’http://fr.safe. vuln.local/reset.php’
0,005467
Temps de réponse avec un compte existant (en secondes) :
curl -i -s -k -w %{time_total}\\n -o /dev/null -X $’POST’ -H $’Content-Type: application/x-www-form-urlencoded’ –data-binary $’email=test%40demo.local&button=’ $’http://fr.vuln.demo.local/reset.php’
0,758968
Nous constatons que l’écart ici est d’environ 700 millisecondes, ce qui correspond au temps que prends l’application pour envoyer le mail. Cet écart est largement suffisant pour procéder à l’énumération des emails fonctionnels.
Pour corriger cet nouvelle énumération, il faut faire en sorte que les emails ne soient pas envoyés directement lors du clic sur le bouton mais soit mis en attente et envoyés par un autre processus qui tournerais par exemple en tache planifié toute les quelques minutes. De cette manière il deviendra réellement impossible d’énumérer les utilisateurs sur cette fonctionnalité.
Formulaire de création de compte
C’est sur les formulaires de création de compte utilisateur que les énumérations sont les plus fréquentes. De la même manière que sur les formulaires de récupération de mot de passe, il arrive souvent que les applications web informent les utilisateurs lorsqu’un identifiant est déjà pris (ici l’adresse email).
Si l’identifiant n’est pas déjà pris, alors la plupart du temps l’application affichera un message ressemblant à celui-ci :
Afin de ne pas provoquer d’énumération utilisateurs, la solution consiste d’une part à bien utiliser l’adresse email comme identifiant, et d’autre part à toujours afficher le même message lorsque le formulaire est validé, même si l’email existe déjà sur l’application, par exemple : « Votre compte a été créé, vous allez recevoir un email de confirmation dans quelques instants ».
Si l’email n’existait pas sur l’application alors le processus ne change pas et on envoie un email indiquant que le compte est créé et qu’il faut cliquer sur un lien pour valider son compte.
Si l’email existait déjà dans la base de données, cela signifie probablement, dans un cas classique, qu’un utilisateur qui avait déjà un compte a essayé d’en recréer un avec la même adresse email. Dans ce cas, le message envoyé sera, par exemple : « Vous avez tenté de créer un compte utilisateur avec cette adresse email mais celle-ci est déjà inscrite sur notre application. Si vous avez oublié votre mot de passe, nous vous invitons à cliquer sur le lien ci-dessous afin de le réinitialiser ».
De cette manière, il ne sera plus possible pour un potentiel attaquant de procéder à une énumération de comptes utilisateurs, mais il sera toujours possible d’informer un utilisateur légitime qu’il tente d’utiliser une adresse déjà existante.
Conclusion
Il est évident que la plus grande difficulté réside plus dans les choix ergonomiques (parcours utilisateurs, messages), qui ne sont pas essentiellement du ressort des développeurs, que dans des choix techniques. Mais bien que les énumérations utilisateurs ne soient pas aussi impactantes que d’autres vulnérabilités telles que des injections SQL ou des XSS, elles restent une trop grande source d’information pour de potentiels attaquants et leur impact est bien trop souvent sous-estimé par les développeurs.
Les corrections à mettre en œuvre n’étant pas réellement complexes, nous ne pouvons qu’encourager à les appliquer, autant que possible.
Auteur : Romain GARCIA