Web authentification / Password reset : REX de Bug Bounty

MISC n° 098 | juillet 2018 | Yann Cam
Creative Commons
  • Actuellement 0 sur 5 étoiles
0
Merci d'avoir participé !
Vous avez déjà noté cette page, vous ne pouvez la noter qu'une fois !
Votre note a été changée, merci de votre participation !
Présentation de faiblesses communément observées lors de recherches de vulnérabilités dans le cadre de Bug Bounty publics et privés, à l’encontre des modules web d’authentification et de réinitialisation de mot de passe.

Mettre en place un mécanisme d’authentification (d’utilisateurs, administrateurs, clients) sur un site web va généralement de pair avec une fonctionnalité de « mot de passe oublié » permettant la réinitialisation du password. Le présent article est voué à dresser une liste (non-exhaustive) des faiblesses communément observées sur de tels modules en agrémentant via des cas concrets issus de Bug Bounty.

1. Authentification web et faiblesses communes

1.1 Énumération passive / OSINT

L’énumération est une des faiblesses les plus répandues au sein des formulaires d’authentification bien que non critique. En effet, il est souvent possible de les exploiter pour déterminer si un login (généralement sous forme d’une adresse e-mail) est associé à un compte ou non. L’intérêt pour l’attaquant est de concevoir une liste de logins valides (sans disposer, encore, de leurs mots de passe respectifs).

Un compte est d’autant plus sécurisé, et donc contribue à la sécurité globale de l’applicatif, lorsque ses deux principales composantes associées à la confidentialité sont inconnues de l’attaquant, à savoir le mot de passe, mais également le login.

L’énumération passive consiste à dresser une liste de logins potentiels (wordlist) à partir de ressources tierces (réseaux sociaux, OSINT), et de les tester un à un (via un script) à l’encontre du formulaire d’authentification.

L’énumération est rendue possible via une distinction comportementale de la réponse du formulaire entre un login existant et inexistant (avec un mauvais mot de passe dans les deux cas).

Cas typique :

  • Tentative avec le login inexistant « unknown@target.com », retour  : « Identifiant inconnu » ou « code HTTP 302 » ;
  • Tentative avec le login existant « admin@target.com », retour de l’application : « Mot de passe incorrect » ou « code HTTP 403 ».

Cette distinction de message d’erreur ou de code retour suffit à l’attaquant pour déterminer si un login soumis est associé à un compte ou non. Il réalise une première fuite d’informations sensibles : une liste d’identifiants pouvant faciliter des attaques ultérieures (spear-phishing, brute-force, social-engineering, post-exploitation sur un périmètre plus large, etc.). C’est une exfiltration indirecte de données du backend.

De nombreux outils facilitent de telles énumérations, tel que theharvester [01], capable de dresser des listes d’adresses e-mails valides associées à un domaine (@target.com) via des techniques d’OSINT (Open Source INTelligence) sur les moteurs de recherche.

Ou encore linkedin2username [02], permettant à partir du réseau social professionnel LinkedIn, de générer des logins probables sur une application corporate via les collaborateurs présents sur ce réseau. Les logins sont généralement sous la forme « pnom », « p-nom », « prénom.nom », etc.

1.2 Énumération active/incrémentale ou déductible

L’énumération active, incrémentale ou déductible permet de gagner en exhaustivité des logins associés à des comptes sur une plateforme ciblée. Si entre deux inscriptions (création de compte), l’attaquant observe que les logins évoluent avec une suite logique (M290690 puis M290691) il peut automatiser la génération de logins pour une énumération.

Celui-ci automatisera les tests d’énumération depuis le login M000000 jusqu’à M999999, et dressera une liste bien plus complète, car le format des identifiants s’avère prédictible.

L’attaquant dispose de l’intervalle des logins de l’applicatif ainsi que du format de ceux-ci, information qui n’est pas d’une forte criticité, mais facilitant grandement ses futures attaques.

1.3 Gestion des sessions

L’identifiant de session, généralement échangé au travers des cookies, est une des données les plus critiques qui doit être soigneusement protégée entre l’usager et l’applicatif.

Dès lors que celui-ci est dérobé, l’attaquant est en mesure de se connecter sous le compte de la victime sans avoir connaissance de son mot de passe. Il usurpe la session (hijacking).

1.3.1 Non-renouvellement avant/après authentification

La valeur du cookie de session doit disposer de plusieurs caractéristiques, hélas, trop peu souvent mises en pratique :

  • Unicité, d’où une entropie/un aléa robuste utilisé lors de sa génération ;
  • Générée et définie côté serveur, sans qu’un attaquant ne puisse définir sa valeur (session fixation attack, HTTP Response Spliting…) ;
  • Limitée dans le temps (ne pas avoir des sessions de 10 ans) ;
  • Être gérée correctement à la création, destruction et renouvellement.

C’est sur ce dernier point qu’une observation souvent faite est à souligner : les applicatifs attribuent un cookie de session dès lors qu’une quelconque page (non-authentifiée) de l’application est atteinte ; et cette valeur du cookie de session est conservée post-authentification.

Intrinsèquement à la notion de « session », il convient d’en générer pour chaque espace. Un cookie pour une session non-authentifiée, renouveler celui-ci (ou générer un nouveau cookie) pour l’espace authentifié standard, et renouveler à nouveau pour des espaces à forts privilèges.

À titre d’exemple, Amazon, la plateforme commerciale, implémente ces mécanismes : si vous êtes un habitué de Amazon, vous devez être constamment authentifié (cookie de session « client simple »). Toutefois, dès lors que vous procédez à un achat et que vous entrez dans le processus de paiement (choix de la carte bancaire, etc.), une réauthentification est nécessaire générant une autre session plus sensible et donc plus courte.

Pour une application, il est donc conseillé de détruire le cookie de session obtenu en mode « non-authentifié », pour en générer un nouveau post-authentification.

1.3.2 Session fixation attack / HTTP Response Spliting

L’attaque par fixation de session, également réalisable par HRS (HTTP Response Spliting), se synthétise par la capacité de l’attaquant à forger une URL ou une requête GET/POST vers un applicatif, pour y définir lui-même la valeur du cookie de session.

Ainsi, l’attaquant génère une URL telle que https://target.com/index.php?sessid=133713371337 et dès lors qu’il accède à cette URL, le serveur lui retourne dans les en-têtes un Set-cookie : SESSID=133713371337.

La dangerosité de cette faiblesse est qu’en incitant une victime à se rendre à cette URL, l’attaquant prédéfinit la valeur de la session de sa victime. L’usager légitime (avec le cookie SESSID=133713371337) s’authentifie sur l’applicatif et l’attaquant n’a plus qu’à utiliser la même valeur du cookie pour usurper son identité (figure 1).

Fig. 1 : Illustration d’une session fixation attack rencontrée sur un bug bounty.

Si les valeurs des cookies de session étaient renouvelées constamment entre un espace non-authentifié et authentifié, ce type d’attaque ne serait pas exploitable.

De plus, permettre de définir via un paramètre GET ou POST la valeur d’un cookie (ou un tout autre header via une HRS, pouvant engendrer des attaques XSS) est une pratique à proscrire.

1.3.3 Prédiction et faiblesse des tokens de sessions

Dans certains cas, la valeur du cookie de session est prédictible, ce qui peut amener à des attaques d’une grande criticité comme le vol de n’importe quel compte utilisateur.

Pour un autre bug bounty, il m’est arrivé de créer plusieurs comptes utilisateurs et de m’authentifier via différents navigateurs en parallèle sur ces comptes. En observant les cookies affectés à chacun d’eux, je me suis rendu compte de leur caractère prédictible :

R4RGMGX2XKFC

R4RGMGX2XKFD

R4RGMGX2XKFE

[...]

Ces valeurs de session sont clairement incrémentées et en conséquence un attaquant peut facilement réaliser un script qui itère sur ces valeurs (R4RGMGX00000 jusqu’à R4RGMGXZZZZZ) en observant les retours de l’application (code HTTP ou code source indiquant « Bonjour login »). Ainsi, il a été  possible de dérober l’accès à de nombreux comptes actuellement connectés à l’applicatif.

La génération de la valeur d’une session doit nécessairement se réaliser à partir d’un aléa (PRNG robuste) tel que /dev/random. L’utilisation du timestamp courant, d’un hachage du login ou de toute autre information d’identité est à bannir.

1.4 Brute-force actif (online)

Le brute-force actif, sollicitant directement l’application cible et le réseau est à différencier du brute-force passif (offline), qui à l’inverse, effectue ses traitements localement lorsque les hashs des mots de passe sont en possession de l’attaquant.

Avec le brute-force actif, l’attaquant teste des couples d’inconnus : un login associé à un mot de passe. Si l’attaquant connaît un login existant, il va se focaliser sur le mot de passe uniquement (via des wordlists par exemple). Si l’attaquant connaît un mot de passe, il va s’attarder à trouver le login correspondant. C’est là où l’intérêt d’avoir un login unique tout aussi confidentiel qu’un mot de passe s’avère robuste.

Les comptes « admin », « administrateur » ou « test » sont souvent des logins préexistants. Couplés à une wordlist issue de leak ou la traditionnelle Rockyou [03], un attaquant peut employer des outils tels que Burp Intruder [04] ou Hydra [05] pour dérouler un brute-force à l’encontre d’une application jusqu’à trouver des mots de passe valides.

Il est impératif de bloquer les tentatives de brute-force au niveau d’une page d’authentification. Soumettre une grande quantité de mots de passe pour un même compte dans un court intervalle, ou tenter de multiples identifications/authentifications à partir d’une même IP source sont des tâches suspicieuses.

Mais que faire en termes d’implémentation lors de détection de brute-force ?

1.4.1 Blocage automatique du compte

« Pour X tentatives d’authentification échouées pour un même identifiant, bloquer le compte ». Cette méthode est déconseillée, car en cas de brute-force par un attaquant, elle impacte l’utilisateur légitime qui ne peut plus s’authentifier. De plus, comment le déverrouillage du compte est-il réalisé ?

Si l’utilisateur peut se débloquer en self-service (envoi d’un e-mail de déblocage), cette solution peut limiter les impacts. Toutefois, l’attaquant peut spécifiquement cibler cet utilisateur, et bombarder son compte pour le bloquer indéfiniment malgré les tentatives de déblocage de l’usager : déni de service du compte.

L’attaquant pourrait automatiser cela sur tous les logins qu’il a pu déterminer par énumération : pour chaque login, réaliser X+1 authentifications en échec pour bloquer les comptes, et ce, continuellement. L’ensemble des comptes serait bloqué, et les usagers n’arriveraient pas à les déverrouiller en self-service : déni de service global.

Si le déverrouillage nécessite une action du support/helpdesk (appeler tel numéro), les équipes pourraient être submergées d’appels, causant un déni de service de l’applicatif et du standard téléphonique également : critique.

Les mires ADFSv2 proposent une authentification web sans anti-brute-force. En conséquence, il est aisé de bloquer en masse des comptes d’un domaine Windows en bombardant une telle page avec des logins valides et mots de passe invalides.

1.4.2 Blocage temporaire du compte

Un blocage temporaire (pendant M minutes) permet de limiter grandement les attaques par brute-force. Mais cela impacte également la productivité de l’utilisateur légitime et son expérience (UX).

Ce type de blocage est à implémenter côté applicatif. En effet, si une application est rattachée à un référentiel d’authentification centralisé (AD, LDAP), le blocage du compte peut être intrinsèque à la technologie d’annuaire (3 échecs de bind successifs bloquent le compte). Ce qui en plus d’impacter l’accès à l’application, impacte également toutes les autres applications exploitant l’annuaire : le scope de l’attaque s’étend.

Quid du temps de blocage ? Si un attaquant a la capacité de tester 100 mots de passe à la seconde, mais qu’il est bloqué tous les 10 essais pendant 10 minutes, il n’arrivera qu’à tester 432 mots de passe à la journée. S’il est bloqué tous les 3 essais pendant 10 secondes, il arrivera à tester 25 920 mots de passe dans la journée.

1.5 Pavés numériques aléatoires

Le milieu bancaire implémente souvent ce type de mécanisme d’authentification : les pavés numériques aléatoires. Partant d’une bonne intention afin de contrer les keyloggers (puisque le mot de passe numérique est entré en « cliquant » sans utiliser le clavier) ainsi que les mouse-recorders (enregistrement des clics de souris impossible puisque le pavé numérique est affiché aléatoirement), les implémentations existantes peuvent néanmoins engendrer des faiblesses de sécurité.

Ces pavés numériques aléatoires indiquent que les mots de passe ne sont constitués que de caractères numériques. Leur longueur est aussi souvent indiquée dans un message explicite « merci d’indiquer votre code à 6 chiffres » : l’attaquant à toutes les informations pour optimiser son brute-force (figure 2).

Fig. 2 : Pavé numérique aléatoire avec détails de la password policy.

Bien que côté client le mot de passe semble entré de manière sécurisée, la requête POST qui émane du navigateur à destination du serveur contient bien trop souvent ce password en clair, et donc aisé à brute-forcer.

Les solutions de pavés numériques aléatoires jugées sécurisées sont celles qui transmettent l’identifiant du pavé généré pour la session courante, ainsi que la combinaison des cases (chiffres) qui ont été cliquées. La vérification est réalisée côté serveur. Trop peu d’implémentations exploitent ce principe.

1.6 Autocomplete, maxlength, pattern et réflexion : éradiquer les XSS

L’attribut HTML autocomplete est conseillé d’être mis sur off pour les champs input de connexion (login et password). Cet attribut empêche le navigateur de conserver une version en clair des crédentiels pour les réinjecter automatiquement à la prochaine connexion.

Pour des applications critiques, dont les mots de passe ne sont pas à éparpiller, cet attribut évite qu’ils soient enregistrés dans les navigateurs (et donc dérobés si la machine est compromise par un attaquant).

Les attributs de la balise input du mot de passe, maxlength ou encore pattern, permettent d’indiquer des indices précieux à l’attaquant sur la politique de mot de passe appliquée (figure 2) :

  • maxlength=6 : le champ du mot de passe n’accepte que 6 caractères maximum ;
  • pattern=[A-Za-z]{3,6} : mot de passe alpha uniquement (lower/upper) de 3 à 6 caractères.

Une autre observation régulièrement faite concernant les pages d’authentification est la « réflexion » du login dans le code source. Si l’usager se trompe de mot de passe, il est redirigé vers la page d’authentification avec un message d’erreur, et son login est souvent pré-renseigné. Seulement, cette réinjection de données POST peut engendrer des vulnérabilités de type XSS directement dans la page d’authentification.

Une telle vulnérabilité XSS permet à un attaquant de modifier le comportement du formulaire d’authentification (vol des crédentiels en clair via un hook), d’activer un keylogger, compromettre tout le navigateur (framework BeEF [06]) et de contourner toutes les protections anti-CSRF en place dans l’application.

Éradiquer les XSS dans l’applicatif tout en équipant tous les formulaires de protection anti-CSRF est une action obligatoire pour tous les applicatifs web d’aujourd’hui.

1.7 Password Policy disclosure et contrôles client-side

Parfois, les contrôles du login ou du mot de passe sont directement implémentés côté client en JavaScript. Intrinsèquement insécurisés (puisque l’attaquant peut visualiser ces contrôles et les modifier), l’application se doit également d’appliquer ces mêmes vérifications côté serveur.

Exposer client-side de tels contrôles (la politique de mot de passe) permet à l’attaquant d’affiner ses attaques brute-force (figure 2).

Dans certains cas, lors de l’absence de contrôle côté serveur, il est possible d’altérer les requêtes de changement de mot de passe (ou d’inscription), pour définir un mot de passe allant totalement à l’encontre de la politique en place, tel que « x » (1 caractère alpha). Ce contournement de politique ne devrait pas être possible.

2. Réinitialisation de mot de passe perdu ou oublié et faiblesses communes

2.1 Énumération active/passive

Au même titre que les formulaires de connexion, les pages de « mot de passe oublié » sont très fréquemment sujettes à des énumérations de comptes actives ou passives.

Lorsque l’usager est invité à indiquer son adresse e-mail, en vue de réinitialiser son mot de passe, l’application peut indiquer un message tel que « un e-mail vient de vous être envoyé » ou « identifiant inconnu ».

Comme détaillé précédemment, cette différenciation permet de déterminer l’existence ou non d’un compte utilisateur sur le système.

Une différence (notable) est toutefois à préciser, contrairement à l’énumération via les pages de connexion : cette présente énumération par le biais du « reset password » n’est pas totalement transparente et invisible pour la victime, puisque celle-ci reçoit un e-mail.

Une telle énumération s’automatise aisément, même en présence de jeton anti-CSRF.

import requests

import sys

import json

if len(sys.argv) > 1:

        file = sys.argv[1]

else:

        print "[*] Usage : python " + sys.argv[0] + " <file>"

        exit(0)

session = requests.Session()

URL = "https://target.fr/api/auth/forgetPassword"

for login in open(file,'r').readlines():

        login = login.strip()

        r = session.post(URL, json={"email": login})

        if "user_not_found" not in r.text:

                print "[+] ["+login+"] exists !"

2.2 Réinitialisation via e-mail

2.2.1 Mailbombing et saturation

La réinitialisation de mot de passe par e-mail implique de fait l’envoi d’e-mails par l’applicatif et la réception de ceux-ci sur la boîte-mail des usagers. Cette boîte-mail est censée vous appartenir et ne pas être partagée/compromise. Autrement dit, l’étape d’identification est assurée, par essence, par l’accès à votre boîte-mail personnelle.

Dès lors qu’un mécanisme de réinitialisation de mot de passe génère des e-mails, plusieurs points de contrôle sont à vérifier, notamment si le formulaire de réinitialisation est appelé plusieurs fois pour un même usager : la victime (utilisateur légitime) ne devrait recevoir qu’un unique e-mail de réinitialisation, même si le formulaire est appelé plusieurs fois. Ou un e-mail par jour par exemple. Dans tous les cas, une limitation d’envoi doit être mise en place.

Si le formulaire est soumis en boucle pour un même usager, les effets de bord suivants peuvent survenir :

  • DoS / saturation de la boîte-mail de l’usager ;
  • DoS / saturation du serveur SMTP applicatif qui se charge de délivrer les e-mails ;
  • Mise sous liste noire (blacklisting) de vos serveurs d’envoi pour activité inhabituelle, entachant l’image de marque, la qualité du service, et la confiance des utilisateurs.

2.2.2 Qualité des e-mails / spam

La qualité des e-mails envoyés par l’applicatif est primordiale. En particulier concernant les e-mails de « réinitialisation de mot de passe » ou de « déverrouillage de compte ». Cette qualité, pour que ces e-mails n’arrivent pas dans les spams de l’usager, dépend d’en-têtes et d’entrées DNS qui peuvent être configurés, notamment DKIM, DMARC et SPF.

Au cours d’un autre bug bounty, j’ai pu observer que les e-mails de « Bienvenue » (lors de l’inscription) arrivaient bien dans ma boîte de réception. Mais à l’inverse, les e-mails de réinitialisation de mot de passe (mot de passe perdu) étaient catégorisés comme spam.

L’attaque mise en œuvre pour exploiter cette faiblesse consistait à :

  • Cibler une victime victim@target.com ;
  • Dérouler le processus de « mot de passe perdu » pour cette victime ;
  • La victime reçoit le mail avec la procédure de renouvellement de mot de passe dans ses spams ;
  • En parallèle, en tant qu’attaquant, j’envoie un e-mail à cette victime via une adresse proche du domaine cible « @target.fr » par exemple, aux couleurs des e-mails légitimes, invitant l’usager à confirmer/redéfinir son mot de passe sur une page que je contrôle.

Mon e-mail en tant qu’attaquant est fiable, car les en-têtes DKIM, DMARC et SPF sont convenablement définies pour mon domaine « target.fr », ce qui n’est pas le cas du domaine ciblé « target.com ». En conséquence, mon e-mail frauduleux arrivera dans la boîte de réception de la victime.

Par déduction, quel e-mail sera le plus crédible aux yeux de notre victime crédule ? Le légitime dans les spams, ou le frauduleux dans la boîte de réception ?

2.2.3 Réception du mot de passe en clair par e-mail

Lors du déroulement d’un processus de « réinitialisation de mot de passe », si l’e-mail reçu sur votre boîte contient un message du type « Votre mot de passe est : MyInitialPassword » avec votre mot de passe à vous, défini lors de l’inscription, alors vous avez du souci à vous faire (tout comme les responsables de l’applicatif).

En effet, si votre propre mot de passe vous est renvoyé en clair :

  • Cela signifie que l’applicatif les conserve en clair, sans hachage quelconque, ou chiffrés de manière réversible ;
  • Les responsables de l’applicatif peuvent connaître tous les mots de passe des usagers/clients ;
  • Si un attaquant met la main sur la base de données de l’application, il dispose d’un leak sensible avec tous les mots de passe en clair des inscrits ;
  • Si vous avez la mauvaise habitude d’utiliser toujours le même mot de passe, considérez tous vos autres comptes online voire votre boîte-mail comme compromis ;
  • En ces heures sombres avec la RGPD, conserver des mots de passe en clair côté backend n’est pas recommandé pour une application…

À l’heure d’aujourd’hui, votre boîte-mail principale est clairement le service le plus sensible dont vous disposez : si celle-ci est compromise, un attaquant pourra y dénicher un très grand nombre d’informations et étendra son périmètre de compromission :

  • Un accès à votre boîte-mail permet de changer les mots de passe (réinitialisation) de la plupart des services en ligne où vous êtes inscrit ;
  • Beaucoup d’e-mails de « rappel de votre mot de passe » s’y trouvent, l’attaquant n’a qu’à fouiller.

Ainsi, dans l’intérêt de conserver la confidentialité de vos informations, les mots de passe d’une application tierce ne sont pas voués à être dupliqués/transférés ailleurs que dans l’application elle-même. Envoyer un mot de passe en clair sur une boîte-mail n’est pas une bonne pratique.

2.2.4 Réception d’un mot de passe auto-généré

La réception d’un mot de passe auto-généré est considérée comme « mieux sécurisée » que la retransmission du mot de passe originel en clair, mais engendre de nouvelles problématiques de sécurité et pas des moindres.

Les mots de passe auto-générés transmis par e-mail induisent toujours le souci de déporter une information hautement confidentielle sur une source tierce, qui n’est pas forcément de confiance (votre boîte-mail), centralisant une nouvelle fois en un même endroit toutes sortes de crédentiels.

Les mots de passe auto-générés informent également de la politique de mot de passe en vigueur. Casse des caractères, chiffres, symboles, longueur prédéfinie : toutes ces informations permettent à l’attaquant d’affûter ses prochaines attaques par brute-force. Par exemple, suite à 3 réinitialisations de mot de passe successives et réception des passwords « XOLIMX », « TOLRAP » puis « MACYAN », on en déduit que les mots de passe auto-générés sont tous en majuscule, sans chiffre ni symbole, et de longueur 6 (brute-force a posteriori aisé).

L’autre problématique bien plus critique avec les mots de passe auto-générés engendre un déni de service d’un ou plusieurs comptes (en fonction de l’énumération réalisée au préalable). Si un attaquant réalise un script qui génère en boucle pour un ou plusieurs comptes des requêtes de « mot de passe oublié », alors :

  • Les usagers légitimes vont recevoir un très grand nombre d’e-mails avec des nouveaux mots de passe auto-générés ;
  • Les mots de passe auto-générés ne seront utilisables qu’un temps très court (tant qu’un nouveau auto-généré n’est pas de nouveau envoyé) ;
  • Le/les usager(s) légitime(s) sont donc dans l’impossibilité de se connecter à leur compte, étant donné que leur mot de passe est auto-généré et change continuellement : déni de service ;
  • La régénération d’un mot de passe par le serveur, et son hachage en base sont des opérations pouvant être coûteuses en ressources (bcrypt). À grande échelle et pour beaucoup de comptes, les performances du serveur peuvent être impactées sévèrement.

2.2.5 Réception d’un lien de réinitialisation

Une des meilleures pratiques consiste à envoyer des e-mails de réinitialisation de mot de passe contenant un lien pointant vers l’application pour précéder au renouvellement. Ces liens disposent généralement d’un token aléatoire via un paramètre GET.

Ce token GET doit nécessairement être valable qu’une seule fois (unicité) et limité dans le temps (quelques dizaines de minutes). Hélas, beaucoup d’implémentations ne respectent pas cela :

  • liens de réinitialisation réutilisables à l’infini avec le même token sans limitation temporelle ;
  • token « aléatoire » qui n’est autre que le MD5 du login (donc calculable pour tous les utilisateurs, avec vol de n’importe quel compte) ;
  • token prédictible basé sur le timestamp au moment de la demande de réinitialisation.

Pour un autre bug bounty d’un site d’e-commerce de renom, j’ai pu observer que le formulaire atteint par le biais d’un tel lien unique, ne validait ni n’exploitait plus du tout ce token ni l’identité l’ayant généré. Il suffisait de soumettre les données POST userid (arbitraire, n’importe quel compte), passwd1 et passwd2 au endpoint /resetpassword et le tour était joué. Quid de l’utilité d’un token aléatoire à usage unique si le formulaire final permet de voler n’importe quel compte client ?

2.3 Autres méthodes de réinitialisation

2.3.1 Questions d’identification

Les « questions personnelles » que vous avez pré-enrôlées sur l’applicatif avec vos propres réponses ne sont plus considérées comme sûres en 2018. En plus d’être un mécanisme de réinitialisation boudé par les usagers en termes d’UX, cette méthode d’identification dépend fortement des questions et des réponses.

C’est justement ces questions et réponses qui peuvent aujourd’hui être mises à mal, notamment avec des approches OSINT et les réseaux sociaux.

  • Quel est le prénom de votre animal de compagnie ?
  • Quel est le nom de jeune fille de votre mère ?
  • Dans quel lycée étiez-vous ?

Ces types de questions peuvent facilement trouver leurs réponses sur des sites tels que Facebook ou Copainsdavant, mettant à mal la sécurité de votre mot de passe en conséquence.

Ces formulaires de questions anodines bien que personnelles, n’étant pas suffisamment suspectes pour vous alerter, peuvent également être exploités via des attaques par clickjacking pour gagner le dernier iPhone si l’applicatif n’est pas protégé contre ce type d’attaque via des en-têtes HTTP tels que X-Frame-Options.

2.3.2 « Merci de contacter le support au numéro suivant »

Lorsque le processus de réinitialisation de mot de passe vous invite à « contacter le support » pour procéder au renouvellement, notamment par téléphone, imaginez la surcharge d’appels téléphoniques du helpdesk si l’ensemble des comptes s’avère bloqué.

Il est recommandé de permettre une autonomie à l’usager final dans sa réinitialisation de mot de passe (self-service), pour soulager les ressources internes de support et éviter les pics d’activité/saturation du réseau téléphonique en cas d’attaque.

2.3.3 Courrier postal avec code d’accès

Cette méthode est également répandue. Déjà rencontrée lors de bug bounty, je me suis retrouvé avec plusieurs lettres similaires reçues quelques jours après mes tests, contenant pour chacune d’elles un « nouveau code d’accès ».

Cette méthode de réinitialisation peut engendrer des coûts supplémentaires particulièrement onéreux pour les propriétaires de l’application en cas de bombardement de demande de réinitialisation pour de multiples usagers.

3. Mitigation, prévention et sécurité

3.1 Anti-robotisation

Pour éviter toutes les attaques et faiblesses détaillées précédemment et automatisables via des scripts (brute-force, blocage de comptes en masse, mailbombing, énumération, etc.), l’anti-robotisation est conseillée. Comprendre : implémenter un Test de Turing, une solution de CAPTCHA.

Une idée reçue, et totalement fausse, concerne les mécanismes anti-CSRF (jeton en session, analyse du referer) : ces protections bien qu’indispensables, ne permettent pas de protéger un module d’authentification contre la robotisation.

Les CAPTCHAs sont rarement appréciés. C’est pourquoi, l’idée est d’implémenter un CAPTCHA qui n’apparaît qu’après X tentatives de soumission d’un formulaire, ainsi l’expérience utilisateur ne serait pas dégradée au départ, mais seulement après plusieurs essais infructueux.

3.2 Limitation de requêtes

En complément du CAPTCHA, appliquer des limites au niveau des requêtes (par utilisateur, ou par adresse IP source) est une excellente pratique :

  • Un même usager ne doit pouvoir réinitialiser son mot de passe qu’une fois par jour ;
  • Une même IP ne doit pouvoir réaliser que X tentatives d’authentifications successives en 1h.

Éviter les blocages automatiques des comptes, en particulier si l’usager n’est pas autonome pour le déverrouiller.

3.3 Limitation et qualité des e-mails

Pour tous les formulaires de réinitialisation exploitant les e-mails :

  • Envoyer des e-mails de qualité qui ne finissent pas dans les spams (DKIM / DMARC / SPF) ;
  • Limiter le nombre d’envoi d’e-mails par utilisateur et par jour ;
  • Ne jamais envoyer un mot de passe en clair (l’initial ou un auto-généré) par e-mail ;
  • Privilégier les liens à usage unique et limités dans le temps pour réinitialiser un mot de passe (bien vérifier que le token est associé au bon utilisateur, et que ce token est généré avec un PRNG robuste).

3.4 En-têtes, cookies et transit

Généraliser l’utilisation de SSL/TLS avec HTTPS, surtout avec la facilité actuelle (LetsEncrypt). Une fois une application accessible en HTTPS, forcer l’utilisation de ce canal (redirection automatique HTTP vers HTTPS), puis activer les sécurités complémentaires :

  • HSTS : HTTP Strict Transport Security (pour forcer le chargement de toute ressource tierce via HTTPS) ;
  • Flag Secure sur les cookies en particulier le cookie de session (pour que les cookies critiques ne transitent jamais en clair sur le réseau) ;
  • Flag HttpOnly sur les cookies en particulier le cookie de session (pour prévenir le vol de cookie par XSS) ;
  • Flag SameSite sur les cookies en particulier le cookie de session (pour bloquer toutes les CSRF) ;
  • X-Frame-Options : pour contrer les attaques par clickjacking.

Tous les autres en-têtes de sécurité sont bien évidemment conseillés, tels que X-XSS-Protection, CSP, X-Content-Type, mais sortent du présent cadre de l’article.

3.5 Messages génériques et politique

Diffuser le moins d’informations publiques possible à propos de la politique de mot de passe en place, en particulier si celle-ci est jugée faible.

De plus, afin de contrer les énumérations, harmoniser les messages retours et messages d’erreur sur les pages d’authentification et formulaires de réinitialisation de mot de passe avec un message générique et unique :

  • Page d’authentification :
    • Éviter les différenciations « Identifiant inconnu » ou « Mot de passe incorrect » ;
    • Privilégier le message unique : « Échec de connexion » ;
  • Page de réinitialisation de mot de passe :
    • Éviter les différenciations « Adresse e-mail inconnue » ou « Un mail a été envoyé » ;
    • Privilégier le message unique : « Un e-mail a été envoyé à l’adresse indiquée si elle est associée à un compte ».

3.6 Identifiants et mots de passe

Privilégier des identifiants uniques non-incrémentals (pour contrer les énumérations actives). Pour les logins au format « e-mail », appliquer correctement la RFC5233 en autorisant le caractère +, alias natif de n’importe quelle boîte-mail, permettant d’avoir des logins uniques (en plus de mot de passe unique évidemment) ; tout en pointant vers la même boîte-mail :

  • yann.cam+linkedin@domain.com (pointant intrinsèquement vers yann.cam@domain.com) ;
  • ann.cam+facebook@domain.com (pointant également vers yann.cam@domain.com).

Cette technique permet d’avoir des couples (login/password) uniques pour chaque service, tout en ayant une seule adresse e-mail de centralisation. Ainsi, dans les leaks massifs médiatisés (HaveIBeenPwned [07]), vos différents comptes seront difficiles à recouper.

Concernant le choix des mots de passe, je ne détaillerais pas le sujet ici, mais en bref : utiliser un Password Manager [08], générez des mots de passe robuste, aléatoires, longs et toujours uniques.

Le service HaveIBeenPwned a mis en place une API permettant de vérifier si un compte ou un mot de passe a déjà fuité dans un leak. Automatiser la vérification du leak d’un mot de passe lors de son changement au travers de ces API est d’intérêt, tout comme confronter le mot de passe à des listes noires connues.

3.7 Approche fonctionnelle robuste

Adopter une approche de « security by design » pour les scénarios fonctionnels. La figure 3 représente les cheminements menant à des faiblesses à éviter.

Fig. 3 : Arbre des faiblesses en fonction des approches fonctionnelles.

Conclusion et remerciements

Concevoir un module d’authentification/réinitialisation de mot de passe n’est pas chose aisée pour le rendre sécurisé. Avoir conscience des attaques possibles et des contres-mesures associées est indispensable pour tous les développements web actuels, l’OWASP est un très bon guide à ce sujet [09] [10].

Je tenais à saluer les diverses plateformes de Bug Bounty et les programmes publics et privés associés, qui m’ont fait confiance pour éprouver leurs systèmes, et qui m’ont ainsi permis de synthétiser plusieurs observations au travers de cet article.

Salutations à toute l’équipe de SYNETIS et à la team Le££e au passage, pour leurs échanges, relectures et motivations !

Références

[01] theharvester, https://tools.kali.org/information-gathering/theharvester

[02] Linkedin2Username - Generate Username Lists For Companies On LinkedIn (OSINT Tool), https://www.kitploit.com/2018/03/linkedin2username-generate-username.html

[03] rockyou wordlist, https://wiki.skullsecurity.org/Passwords

[04] Burp, https://portswigger.net/burp

[05] THC-Hydra, https://tools.kali.org/password-attacks/hydra

[06] BeEF framework, http://beefproject.com/

[07] HaveIBeenPwned, https://haveibeenpwned.com/

[08] Password Manager, https://keepass.info/

[09] OWASP Authentication Cheat Sheet, https://www.owasp.org/index.php/Authentication_Cheat_Sheet

[10] OWASP Forgot Password Cheat Sheet, https://www.owasp.org/index.php/Forgot_Password_Cheat_Sheet