Wachtwoord vergeten is het gat waar iedereen doorheen komt
Zes fouten in een herstelmail die AI standaard maakt, met per fout waarom hij een account kost.
- SoortChecklist
- Tijd20 minuten
- Voor wieIedereen die zelf inloggen heeft gebouwd in plaats van het uit te besteden
- Fases3
Iedereen bouwt inloggen zorgvuldig. Wachtwoorden worden gehasht, sessies verlopen, er zit een limiet op inlogpogingen. En dan komt de wachtwoord-vergeten-flow er in vijf minuten achteraan, want dat is maar een mailtje. Die flow is een tweede voordeur, en wie daar doorheen komt hoeft je wachtwoord niet te kennen.
Deze handleiding is geschreven om als werkopdracht te dienen. Neem hem in zijn geheel mee naar Cursor, Claude Code of waar je ook bouwt, en werk hem fase voor fase af.
Waar het misgaat
Fase 0Vaststellen
Laat de flow nakijken voordat je hem aanpast. Vraag om bestand en regelnummer, anders krijg je een verhaal.
Kijk in deze codebase naar de volledige wachtwoord-vergeten-flow, van het aanvraagformulier tot het moment dat het nieuwe wachtwoord is opgeslagen. Controleer per punt en geef bestand en regelnummer: 1. Waarmee wordt het reset-token gegenereerd? Is dat een cryptografisch veilige bron? 2. Heeft het token een vervaltijd, en wordt die ook echt gecontroleerd bij gebruik? 3. Wordt het token na gebruik verwijderd of kan het opnieuw? 4. Wordt het token gehasht opgeslagen of leesbaar? 5. Geeft de aanvraagroute een ander antwoord voor een bestaand dan voor een onbekend e-mailadres? Kijk ook naar de statuscode en naar het aantal databasebevragingen. 6. Zit er een rate-limit op de aanvraagroute, per adres en per IP? 7. Worden bestaande sessies ingetrokken na een geslaagde reset? Geef per punt: goed, fout, of niet gevonden. Bij fout: de kleinst mogelijke wijziging die het oplost. Verzin geen bestandsnamen.
Fase 1De zes fouten
Op volgorde van hoe vaak ze voorkomen. De laatste doet het meeste kwaad.
- Je token is voorspelbaarMath.random() en Date.now() zijn niet willekeurig genoeg om als sleutel te dienen. Gebruik crypto.randomBytes(32).toString('hex') of crypto.randomUUID().
- Het token blijft geldigZet er een vervaltijd op van vijftien tot zestig minuten en maak hem eenmalig. Na gebruik verwijderen, niet op gebruikt zetten en bewaren.
- Het token staat leesbaar in je databaseBehandel hem als een wachtwoord: sla de hash op. Lekt je database, dan kan iemand met de tokentabel anders elk account resetten dat op dat moment een reset open had staan.
- Je vertelt of het e-mailadres bestaatGeef altijd hetzelfde antwoord: als dit adres bij ons bekend is, is er een mail onderweg. Let ook op je reactietijd; een halve seconde verschil verklapt het alsnog.
- Er zit geen limiet opZonder limiet kan iemand duizend resetmails naar hetzelfde adres sturen. Dat is pesten, het kost jou geld per mail, en het is de snelste manier om op een spamlijst te komen. Drie per adres per uur is ruim.
- De oude sessies blijven gewoon draaienIemand reset zijn wachtwoord omdat hij dénkt dat er is ingebroken. De inbreker blijft ingelogd, want zijn sessie is nooit ingetrokken. Trek bij een geslaagde reset alle sessies in, op het huidige apparaat na.
Fase 2Controleren
Bewijs voordat je zegt dat het klaar is. Dit doe je met twee testaccounts en een klok.
- Een gebruikt token werkt niet nog een keerReset je wachtwoord, en open daarna dezelfde link uit de mail opnieuw. Die moet weigeren.
- Een oud token werkt niet meerVraag een reset aan, wacht tot na je vervaltijd, en probeer de link. Kun je niet wachten, zet de vervaltijd tijdelijk op één minuut.
- Een onbekend adres krijgt hetzelfde antwoordVraag een reset aan voor een adres dat niet bestaat. Zelfde tekst, zelfde statuscode, en ongeveer dezelfde reactietijd.
- De vierde aanvraag binnen een uur wordt geweigerdVraag vier keer achter elkaar een reset aan voor hetzelfde adres.
- Een sessie op een ander apparaat is na de reset verlopenLog in op je telefoon, reset op je laptop, en herlaad op je telefoon. Je moet eruit liggen. Dit is de belangrijkste van de vijf.
- Het token in je database is niet leesbaarKijk in de tabel. Zie je de waarde die ook in de mail stond, dan sla je hem ongehasht op.
Het eerlijkste advies: bouw dit niet zelf. Supabase Auth, Clerk en Auth0 hebben deze zes punten allemaal al goed staan, en je hebt er niets aan om ze opnieuw te leren kennen via een incident.
Alles hierboven in één keer, inclusief de fases, de SQL en de controlestap.
Werkt dit, maar wil je het niet zelf doen?
Dit blad is gratis en blijft gratis. Wil je dat het bij jou automatisch draait in plaats van met de hand, dan bouw ik dat. Beschrijf in vijf vragen wat er vastloopt en stuur het adres van je app mee; binnen één werkdag heb je mijn antwoord op papier.
Vertel wat er vastloopt