Reset hesla e-mailem bezpečně: jednorázový token, expirace, rate limit a co napsat do zprávy
Kontrolní seznam pro obnovu hesla přes e-mail: náhodný token uložený jako hash, krátká platnost, jedno použití, stejná odpověď pro existující i neexistující účet, omezení pokusů, odkaz vs. kód a text e-mailu, který neskončí ve spamu.
Reset hesla je nejcitlivější e-mail, který aplikace posílá: kdo ho zachytí nebo vyvolá, získá účet. Přitom je to jedna z prvních funkcí, které vývojář napíše, často večer před spuštěním. Správná implementace má šest pravidel a žádné z nich není složité; tady jsou v pořadí, v jakém se na nich nejčastěji chybuje.
1. Token: dlouhý, náhodný, uložený jako hash
Odkaz pro reset obsahuje token, který je jediným důkazem, že žadatel má přístup ke schránce. Proto:
- Alespoň 32 bajtů z kryptografického generátoru:
crypto.randomBytes(32)v Node.js,random_bytes(32)v PHP,secrets.token_urlsafe(32)v Pythonu. Nikdy ID uživatele, časové razítko, ani jejich hash; obojí lze odhadnout. - Do databáze ukládejte hash tokenu (SHA-256 stačí, token má dost entropie), ne token samotný. Pokud unikne databáze nebo logy, útočník nemá platné odkazy.
- K hashi uložte
user_id,created_at,expires_at,used_ata případně IP a user agent žádosti. - Porovnávejte v konstantním čase (
hash_equals(),crypto.timingSafeEqual()), i když u 256bitového tokenu je riziko časového útoku spíš teoretické.
Token patří do cesty nebo query odkazu, ne do uživatelského jména, a odkaz by měl vést na stránku, která token nikam dál nepošle: nastavte na ní Referrer-Policy: no-referrer, nenačítejte na ní cizí skripty ani obrázky z externích domén, aby token neodtekl v hlavičce Referer.
2. Platnost: krátká, jednorázová, poslední vyhrává
- 15 až 60 minut. Reset hesla uživatel dělá teď, ne zítra. Hodina pokryje zpožděný e-mail i hledání ve spamu; delší platnost rozšiřuje okno pro zneužití.
- Jedno použití. Po nastavení hesla označte token jako použitý a všechny ostatní nevyužité tokeny stejného uživatele zneplatněte.
- Nový token zneplatní starý. Když uživatel klikne „poslat znovu", starý odkaz přestane platit. Jinak platí dva a schránka plná starých e-mailů je sbírka klíčů.
- Po změně hesla ukončete ostatní přihlášení (invalidujte session i refresh tokeny). Smysl resetu často je vyhodit někoho, kdo se do účtu dostal.
3. Neprozrazujte, zda účet existuje
Formulář „zapomenuté heslo" musí odpovědět stejně pro registrovanou i neregistrovanou adresu:
Pokud u nás máte účet s touto adresou, poslali jsme vám odkaz pro nastavení nového hesla. Zkontrolujte i složku spam.
Jakmile odpovíte „tato adresa není registrována", máte veřejný nástroj na ověřování, kdo je vaším zákazníkem. Stejně se chovejte v časování odpovědi (odesílání e-mailu dělejte asynchronně, aby nebyla registrovaná adresa poznat podle pomalejší odpovědi) a v API: stejný stavový kód, stejné tělo.
Na neexistující adresu neposílejte nic. Některé velké služby posílají i e-mail „někdo žádal o reset, ale účet s touto adresou nemáme"; pro většinu aplikací je to zbytečný e-mail navíc a potenciální cesta ke spamu přes váš formulář.
4. Rate limit na tři úrovně
Formulář pro reset je veřejný endpoint, který na požádání posílá e-maily. Bez omezení z něj kdokoli udělá nástroj na zahlcení cizí schránky, a vy za každý e-mail platíte a sbíráte stížnosti. Omezte:
- Na účet: maximálně 3 až 5 žádostí za hodinu. Další žádosti tiše ignorujte (odpověď zůstává stejná).
- Na IP adresu: desítky žádostí za hodinu přes různé adresy jsou skript, ne zákazník.
- Na ověření tokenu: po několika neplatných tokenech z jedné IP zpomalte nebo zablokujte; u 256bitového tokenu hádání nehrozí, ale zbytečně zatěžuje databázi a logy.
Pokud používáte CAPTCHA, dejte ji až po překročení limitu, ne na první pokus; člověk, který zapomněl heslo, má už tak špatný den.
5. Odkaz nebo kód
Odkaz je výchozí volba: jedno kliknutí, token se nepřepisuje, funguje na všech zařízeních. Má dvě slabiny: bezpečnostní brány u firemních příjemců odkazy preventivně otevírají (proto nikdy neměňte heslo ani nezneplatňujte token na GET, ale až na odeslání formuláře), a odkaz nefunguje, když uživatel čte e-mail na jiném zařízení, než kde se přihlašuje.
Kód (6 až 8 číslic opsaných do formuláře) řeší druhé zařízení a obchází brány, ale potřebuje vlastní rate limit na pokusy, protože kombinací je málo. Pravidla pro kódy jsou v článku o ověřovacím kódu e-mailem.
Rozumný kompromis: odkaz, který obsahuje token, a pod ním stejný token zobrazený jako kód pro ruční opsání. Nebo nabídnout obojí a nechat uživatele vybrat.
6. Co napsat do e-mailu
Reset hesla je zároveň nejčastější námět phishingu, takže váš e-mail musí být rozpoznatelný jako pravý a nesmí učit zákazníky klikat na cokoli.
Předmět: konkrétní a klidný. „Nastavení nového hesla k účtu Firma" ano; „URGENT: Your account will be deleted" ne. Předmět, který vzbuzuje paniku, je přesně to, co dělá phishing, a filtry to vědí.
Tělo:
- Co se stalo: „Někdo (nejspíš vy) požádal o nastavení nového hesla k účtu na firma.cz pro adresu jan@…".
- Jedno tlačítko nebo odkaz s viditelnou plnou adresou na vaší doméně; žádné zkracovače, žádné sledovací přesměrování přes cizí doménu.
- Doba platnosti: „Odkaz platí 30 minut a lze ho použít jednou."
- Co dělat, když to nebyl uživatel: „Pokud jste o změnu nežádali, nic se nestalo a heslo zůstává stejné. Tento e-mail můžete smazat." Nepřidávejte odkaz „nahlásit"; další odkaz je další příležitost pro phishing.
- Odesílatel s jménem vaší služby a adresou na ověřené doméně (
ucet@firma.cz), nenoreply@na doméně poskytovatele.
Co do e-mailu nepatří: heslo (ani dočasné), uživatelské jméno v kombinaci s odkazem, reklama, odhlašovací odkaz (není z čeho se odhlásit), a žádné přílohy.
Po úspěšné změně pošlete druhý e-mail: „Heslo k vašemu účtu bylo změněno dne … z IP …. Pokud to nebyli vy, kontaktujte nás." Tenhle e-mail je často jediný způsob, jak se oběť o převzetí účtu dozví.
Doručitelnost: reset musí dojít hned
Uživatel sedí u formuláře a čeká. Reset, který dorazí za 20 minut nebo skončí ve spamu, je z pohledu zákazníka nefunkční funkce a z pohledu podpory ticket. Proto:
- posílejte z ověřené domény se SPF, DKIM a DMARC, ne z webového serveru přímo,
- posílejte přes frontu asynchronně, ale bez zbytečné prodlevy; uživatelský dotaz nesmí čekat na SMTP,
- sledujte u těchto e-mailů stav
deliveredabouncedpřes webhook; ubouncedzobrazte uživateli na stránce, že adresa není dostupná, a nabídněte podporu, - označte je značkou (
type=password-reset), abyste je v doručenkách odlišili od ostatních a viděli jejich doručitelnost zvlášť.
Shrnutí
- Token 32+ bajtů z CSPRNG, v databázi jen hash, odkaz bez úniku přes Referer.
- Platnost 15 až 60 minut, jedno použití, nový zneplatní starý, po změně hesla odhlásit všude.
- Stejná odpověď bez ohledu na existenci účtu, včetně časování.
- Rate limit na účet, IP i ověření tokenu.
- Odkaz jako výchozí, kód jako doplněk; heslo se nikdy nemění na GET.
- E-mail: klidný předmět, jeden odkaz na vlastní doméně, doba platnosti, co dělat, když to nebyl uživatel, a potvrzení po změně.
Reset hesla je typický transakční e-mail, který zákazník čeká a který musí dorazit do minuty. Přes TopEmail jde posílat z vaší domény přes API i SMTP relay; cena je stejná jako u ostatních e-mailů, viz ceník.
Transakční e-maily z vaší domény přes API nebo SMTP, od 0,025 do 0,06 Kč za e-mail podle objemu, prvních 1 000 e-mailů měsíčně zdarma. Registrace zdarma, bez platební karty.
Registrovat zdarma →