Le lien que vous pouvez écrire vous-même : dériver un jeton de réinitialisation depuis le code source
Le défi
La réinitialisation de mot de passe de Brightloom coche toutes les cases des référentiels. Elle ne dit pas si une adresse est enregistrée, le lien expire en trente minutes, et il part vers l'adresse au dossier. Il n'a pourtant jamais besoin de vous parvenir, parce que le jeton envoyé est calculé et non généré. Trois fichiers source et un export du support depuis la console d'administration sont ici. Déterminez le jeton que le serveur enverrait pour le compte des opérations, [email protected], et écrivez-le exactement comme le serveur le ferait.
Ce que tu vas apprendre
- Suivre quel générateur un flux appelle réellement quand plusieurs se ressemblent
- Dériver la valeur d'un jeton en exécutant une chaîne de format à la main
- Expliquer pourquoi un jeton de réinitialisation doit être imprévisible, pas seulement unique
- Identifier les champs qu'un attaquant peut déjà voir comme source d'entropie d'un jeton
- Reconnaître que confirmer un jeton sans session supprime la dernière barrière
Compétences testées
Prérequis
- Lire du Python
- Le fonctionnement de strftime et de l'opérateur modulo
Comment ça marche
Un lien de réinitialisation de mot de passe est une capacité. Le détenir revient à prouver que vous contrôlez la boîte mail à laquelle il a été envoyé, c'est pourquoi la valeur doit être imprévisible pour quiconque n'a pas reçu l'e-mail. L'unicité est une propriété de base de données et n'a rien à voir avec cela : des entiers séquentiels sont uniques aussi.
Ce générateur tire ses deux composants de champs qui sont affichés dans une console de support, sur les factures et dans l'interface d'administration. La date d'inscription et le numéro de compte ne sont pas des secrets et n'ont jamais été censés l'être. Les injecter dans une chaîne de format produit une valeur unique par compte et calculable par quiconque a déjà vu une fiche client, un groupe bien plus large que les personnes ayant accès à la boîte mail.
Ce qui aggrave les choses, ce sont les contrôles qui l'entourent. Pas d'énumération de comptes, une expiration de trente minutes, une livraison à la seule adresse enregistrée : chacun de ces contrôles est correct, et chacun d'eux suppose que le jeton est la partie difficile. Quand le jeton peut être calculé, ils protègent la mauvaise chose, et le fait que l'endpoint confirm recherche un jeton sans session associée est ce qui transforme le calcul en prise de contrôle de compte.
Erreurs fréquentes
- Utiliser la colonne id. La ligne des opérations a
id7741 etaccount_no40218. Le générateur litaccount_no. - Prendre la date depuis la ligne de facturation. Elle partage la date d'inscription du 2024-03-11 mais a un numéro de compte différent.
- Prendre les chiffres de Rosa Castellanos. Son
account_no30218 se termine aussi par 0218, ce qui explique pourquoi une mauvaise ligne reste plausible. - Utiliser invite_token. Elle se trouve juste au-dessus de celle qui compte et utilise le jour de l'année sur trois chiffres. Le flux de réinitialisation ne l'appelle pas.
- Supprimer le zéro initial. Le format est
%04d, donc c'est0218et non218. - Se rassurer avec
secrets.token_urlsafe. Elle se trouve dans le même fichier et sert aux sessions, pas aux réinitialisations.
Comment s'en protéger
Générez le jeton, ne le dérivez pas.
- Utilisez une valeur aléatoire cryptographiquement sûre d'au moins 128 bits, par exemple
secrets.token_urlsafe(32), que ce fichier importe déjà pour les sessions. - Stockez un hash du jeton plutôt que le jeton lui-même, pour qu'une lecture de la base de données ne livre pas des liens de réinitialisation actifs.
- Liez confirm à autre chose que le seul jeton : limitez fortement le débit, invalidez toutes les autres sessions en cas de succès, et envoyez un e-mail au compte lors d'un changement de mot de passe.
- Invalidez les jetons en circulation quand un nouveau est demandé, et gardez une expiration courte. Les deux sont peu coûteux et limitent la fenêtre d'exposition.
- Traitez BILL-1877 comme la découverte qu'elle est. Une ligne de réinitialisation consommable par quiconque en connaît le nom, sans limitation de débit, est la seconde moitié de ce bug.