Wachtwoordreset-e-mails die gebruikers vertrouwen (en in de inbox belanden)

Bouw een veilige flow voor wachtwoordresets: eenmalige tokens, korte geldigheid, geen accountenumeratie, een duidelijke template en een bevestiging na de wijziging.

De wachtwoordreset-e-mail is de loper van je product. Wie hem kan lezen, kan het account overnemen. Daarom hebben aanvallers het erop gemunt, en daarom zijn gebruikers er wantrouwig tegenover geworden.

Een goede resetflow moet veilig genoeg zijn om die aanvallers te weerstaan en duidelijk genoeg om binnen een paar seconden het vertrouwen van echte gebruikers te winnen. Deze gids loopt beide door, vanaf het moment dat iemand op “Wachtwoord vergeten?” klikt tot de bevestigingsmail na de wijziging.

De flow in één oogopslag

  1. De gebruiker vult zijn e-mailadres in op de pagina Wachtwoord vergeten.
  2. Je app toont altijd dezelfde melding, of het account nu bestaat of niet.
  3. Bestaat het wel, dan maak je een eenmalig token aan en mail je een resetlink.
  4. De gebruiker opent de link, kiest een nieuw wachtwoord, en het token is verbruikt.
  5. Je logt de andere sessies van de gebruiker uit en stuurt een e-mail “je wachtwoord is gewijzigd”.

Stap 1: Verraad niet wie een account heeft

Reageer na het versturen van het formulier altijd met dezelfde tekst:

Als er een account bestaat voor ada@example.com, hebben we een link gestuurd om je wachtwoord te resetten. De link verloopt over 30 minuten.

Toon je in plaats daarvan “Geen account met dat e-mailadres”, dan vertel je iedereen met een lijst adressen precies welke daarvan je product gebruiken. Houd ook de responstijd gelijk: zoek het account op en verstuur de e-mail op de achtergrond, zodat de pagina niet merkbaar sneller antwoordt voor onbekende adressen.

Stap 2: Maak een sterk, eenmalig token aan

Het token in de link moet lang en willekeurig zijn, en onmogelijk te raden:

import { randomBytes, createHash } from 'node:crypto';

const token = randomBytes(32).toString('base64url');            // goes in the email
const tokenHash = createHash('sha256').update(token).digest('hex'); // goes in the database

await db.passwordResets.upsert({
  userId: user.id,
  tokenHash,
  expiresAt: new Date(Date.now() + 30 * 60 * 1000), // 30 minutes
});

De regels:

  • Sla alleen een hash op van het token, zodat een databaselek geen werkende resetlinks oplevert.
  • Eén actief token per gebruiker. Een nieuw token vervangt het oude.
  • Korte geldigheid: 15 tot 60 minuten is gebruikelijk.
  • Eenmalig gebruik. Verwijder het token zodra het wachtwoord is gewijzigd.
  • Maak het ongeldig als de gebruiker in de tussentijd succesvol inlogt of zijn e-mailadres wijzigt.

Stap 3: Verstuur de e-mail meteen

De gebruiker zit te wachten, dus verstuur in het verzoek zelf, niet in een batchjob:

import os
import requests

def send_reset_email(user, token):
    link = f"https://app.yourco.com/reset-password/{token}"
    res = requests.post(
        "https://backend.inbixio.com/api/send-email/",
        headers={"X-API-Key": os.environ["INBIXIO_API_KEY"]},
        json={
            "to_email": user.email,
            "subject": "Reset your YourApp password",
            "message": render_reset_template(name=user.first_name, link=link, minutes=30),
        },
        timeout=10,
    )
    res.raise_for_status()

Verstuur vanaf een apart, geauthenticeerd adres zoals security@yourco.com. Koppel die mailbox in Inbixio als eigen e-mailserver en geef de authenticatieservice een API-sleutel die daarop is vastgezet, zodat resetmails komen van het adres dat gebruikers verwachten. Verwijder je die server ooit, zet de sleutel dan opnieuw vast. Zie beveiliging van API-sleutels om dit in te richten.

Stap 4: Schrijf een resetmail die mensen vertrouwen

Phishingmails imiteren wachtwoordresets vaker dan welk ander bericht ook. Maak de jouwe onmiskenbaar:

  • Onderwerp: eenvoudig en specifiek, bijvoorbeeld “Reset je wachtwoord voor YourApp”. Geen haast, geen uitroeptekens.
  • Eén duidelijke knop, plus de volledige link als tekst voor clients die knoppen blokkeren.
  • De geldigheid: “Deze link verloopt over 30 minuten.”
  • Context: wanneer het verzoek is gedaan en, als je die informatie hebt, vanuit welke browser of stad.
  • Wat te doen als de gebruiker het niet zelf was: “Heb je niet gevraagd om je wachtwoord te resetten? Dan kun je deze e-mail negeren. Je wachtwoord verandert niet.”
  • Geen andere links, afbeeldingen of aanbiedingen. Hoe minder links, hoe makkelijker de e-mail te controleren is.
<div style="font-family:system-ui,sans-serif;max-width:520px;margin:auto;color:#111;line-height:1.5">
  <p>Hi Ada,</p>
  <p>We received a request to reset the password for your YourApp account.</p>
  <p style="margin:28px 0">
    <a href="https://app.yourco.com/reset-password/TOKEN"
       style="background:#584efe;color:#fff;padding:12px 20px;border-radius:8px;text-decoration:none;font-weight:600">
      Choose a new password
    </a>
  </p>
  <p>This link expires in 30 minutes and can only be used once.</p>
  <p>If you didn't ask to reset your password, you can safely ignore this email.</p>
  <p style="color:#666;font-size:13px">Button not working? Paste this link into your browser:<br>
    https://app.yourco.com/reset-password/TOKEN</p>
</div>

Wanneer de gebruiker de link opent:

  • Zoek de hash op van het token, controleer of het niet verlopen is en toon het formulier voor een nieuw wachtwoord. Log de gebruiker nog niet in.
  • Houd het token buiten verzoeken naar derden. Voeg <meta name="referrer" content="no-referrer"> toe aan de resetpagina en laad daar geen analytics- of advertentiescripts, zodat het token in de URL niet via referrer-headers uitlekt.
  • Bij het versturen controleer je het nieuwe wachtwoord aan de hand van je wachtwoordregels, hash je het met een traag algoritme (bcrypt, scrypt of Argon2), verwijder je het token en log je alle andere sessies uit.

Stap 6: Bevestig de wijziging

Stuur na een wachtwoordwijziging altijd een korte bevestiging:

Je wachtwoord voor YourApp is gewijzigd op 28 oktober om 14:32 UTC. Was jij dit, dan hoef je verder niets te doen. Was jij het niet, beveilig je account dan meteen.

Deze e-mail is de alarmbel voor de gebruiker als iemand anders is binnengekomen, dus sla hem nooit over.

Zorg dat hij aankomt

Een resetmail in de spamfolder is een supportticket. Voor de lancering:

  • stel SPF, DKIM en DMARC in voor je verzenddomein,
  • test met Gmail- en Outlook-adressen en controleer of de headers PASS tonen, en
  • houd na het deployen het percentage Failed Deliveries (mislukte bezorgingen) op je Inbixio-dashboard in de gaten.

Checklist

  • Dezelfde respons voor bestaande en onbekende e-mailadressen
  • Willekeurig token van 32 bytes, alleen de hash opgeslagen
  • Geldigheid van 15–60 minuten, eenmalig gebruik, één actief token per gebruiker
  • Direct verstuurd vanaf een aparte, geauthenticeerde afzender
  • Duidelijke template: één knop, gewone link, geldigheid, regel “was jij dit niet?”
  • Geen scripts van derden op de resetpagina; referrer uitgeschakeld
  • Andere sessies uitgelogd en een bevestigingsmail verstuurd na de wijziging

Verstuur het met Inbixio

Koppel je eigen SMTP-server, maak een API-sleutel met beperkte rechten aan en verstuur vanuit elke programmeertaal met één HTTPS-verzoek.

Account aanmaken Bekijk prijzen