Ověřovací kód e-mailem (OTP): délka, expirace, rate limit a kdy radši SMS
Jak správně implementovat jednorázový kód posílaný e-mailem: generování, uložení, platnost, omezení pokusů, text zprávy. A upřímně o tom, kdy je e-mail špatný druhý faktor a kdy sáhnout po SMS.
Šestimístný kód e-mailem je nejlevnější způsob, jak ověřit, že adresa patří člověku, který se registruje, mění heslo nebo potvrzuje platbu. Implementace vypadá triviálně: vygenerovat číslo, poslat, porovnat. Produkční verze má ale pár míst, kde se dá udělat chyba, která buď otevře dveře útočníkovi, nebo zákazníka vyžene. Tohle je kontrolní seznam, který používáme my, a na konci upřímné srovnání se SMS.
K čemu je e-mailové OTP vhodné a k čemu ne
E-mail dokazuje, že uživatel má přístup ke schránce. To stačí pro:
- ověření adresy při registraci,
- obnovu hesla (tady je to dokonce standard),
- potvrzení citlivé změny (nový e-mail, nové platební údaje),
- přihlášení bez hesla (magic link nebo kód).
Nehodí se jako druhý faktor k heslu, pokud uživatel používá stejné heslo i do e-mailu, což je bohužel častý případ. NIST ve svých doporučeních (SP 800-63B) e-mail jako samostatný druhý faktor neuznává právě proto, že není vázaný na konkrétní zařízení. Pro skutečnou dvoufaktorovou autentizaci použijte TOTP aplikaci, passkey nebo SMS na ověřené číslo.
Generování a formát kódu
- 6 číslic. Čtyři jsou málo (10 000 kombinací), osm uživatelé přepisují s chybami. Šest je kompromis, který uživatelé znají z bank.
- Kryptograficky bezpečný generátor:
crypto.randomInt(0, 1000000)v Node.js,random_int()v PHP,secrets.randbelow()v Pythonu. NikdyMath.random()anirand(). - Doplnit nuly zleva. Kód
004217je platný kód, ne chyba. - Žádná písmena. Alfanumerické kódy mají víc kombinací, ale uživatel na mobilu přepíná klávesnici a plete O a 0. Bezpečnost řeší rate limit, ne entropie kódu.
Uložení: hash, ne prostý text
Do databáze ukládejte hash kódu (SHA-256 s náhodnou solí stačí; bcrypt je u šestimístného čísla zbytečný), ne kód samotný. Pokud někdo získá přístup k databázi nebo k logům, nemá rovnou platné kódy. K hashi uložte:
user_id, code_hash, purpose, created_at, expires_at, attempts, used_at
purpose je důležitý: kód vydaný pro reset hesla nesmí jít použít pro potvrzení platby.
Platnost a jednorázovost
- Platnost 5 až 10 minut. E-mail může dojít s malým zpožděním (greylisting, fronta u příjemce), takže 60 sekund jako u SMS je málo. NIST uvádí 10 minut jako horní hranici pro kódy doručované mimo pásmo.
- Nový kód zneplatní starý. Když uživatel klikne „poslat znovu", starý kód přestane platit. Jinak platí dva a útočník má dvojnásobnou šanci.
- Jedno použití. Po úspěšném ověření nastavte
used_ata kód dál nepřijímejte. - Porovnávejte v konstantním čase (
hash_equals(),crypto.timingSafeEqual()), aby se podle doby odpovědi nedalo poznat, kolik číslic sedí.
Rate limit: nejdůležitější část
Šest číslic je milion kombinací. Bez omezení pokusů je to pro skript otázka minut. Omezte:
- Pokusy na kód: po 5 chybných pokusech kód zneplatněte a vyžádejte nový.
- Vydávání kódů na účet: maximálně 3 až 5 kódů za 15 minut. Jinak si útočník může z tlačítka „poslat znovu" udělat nástroj na zahlcení cizí schránky, a vy platíte každý e-mail.
- Vydávání kódů z IP adresy: ochrana proti hromadnému zkoušení přes mnoho účtů.
- Čekání mezi odesláními: tlačítko „poslat znovu" aktivní až po 30 až 60 sekundách; ukažte odpočet.
Při překročení vracejte stejnou odpověď jako při úspěchu („pokud účet existuje, poslali jsme kód"), aby se podle reakce nedalo zjistit, které adresy jsou registrované.
Text e-mailu
Doručitelnost ověřovacích e-mailů je obvykle výborná (uživatel je čeká), ale pár věcí ji zhorší:
- Kód do předmětu:
482 913 je váš ověřovací kód pro Muj-eshop.cz. Uživatel ho vidí v notifikaci, nemusí e-mail otvírat. Mezera uprostřed pomáhá čtení. - Žádné odkazy a obrázky navíc. Jen kód, pro co platí, jak dlouho, a věta „pokud jste o kód nežádali, ignorujte tento e-mail". Čím míň HTML, tím míň důvodů pro spamový filtr.
- Neposílejte kód a odkaz zároveň. Vyberte jedno; dva mechanismy jsou dvě útočné plochy.
- Odesílatel, kterému se dá věřit:
overeni@vas-eshop.czz ověřené domény s DKIM, nenoreply@nejakasluzba.com. Phishingové e-maily vypadají přesně jako OTP, takže vaše musí vypadat správně. - Transakční infrastruktura. OTP posílejte přes stejnou cestu jako potvrzení objednávek, ne přes newsletterový nástroj; o rozdílu a oddělení provozu píšeme tady.
Kdy radši SMS
E-mail má jednu slabinu: rychlost není garantovaná. Většina kódů dojde do pár sekund, ale greylisting nebo plná fronta u příjemce udělají z 5 sekund 5 minut, a uživatel mezitím odejde. SMS přes přímé napojení na operátory dorazí typicky do několika sekund a telefon má uživatel v ruce. SMS zvolte, když:
- uživatel není u e-mailu (potvrzení na pokladně, přihlášení v mobilní aplikaci, kurýr u dveří),
- potřebujete druhý faktor nezávislý na hesle (schránka může být kompromitovaná stejným heslem),
- ověřujete telefonní číslo, ne e-mail, třeba kvůli doručení zásilky,
- jde o platbu nebo změnu s vysokým dopadem, kde cena SMS (u TopSMS.cz od 0,88 Kč) nehraje roli proti škodě.
E-mail zvolte, když ověřujete e-mail, když cena za ověření musí být blízko nule (0,06 Kč u nás proti necelé koruně za SMS), nebo když uživatel sedí u počítače a stejně schránku má otevřenou. Nejlepší řešení pro aplikace s oběma kanály: primárně e-mail, SMS jako záloha, když si uživatel po minutě požádá o kód znovu. Zákazníkům TopSMS nabídneme oba kanály z jednoho účtu, takže přepnutí je otázka jednoho parametru. Praktický návod na SMS OTP včetně kódu najdete na blogu TopSMS.
Shrnutí
- 6 číslic z bezpečného generátoru, uložit jako hash s účelem.
- Platnost 5 až 10 minut, nový kód ruší starý, jedno použití.
- 5 pokusů na kód, limit vydávání na účet i IP, čekání mezi odesláními.
- Kód v předmětu, minimum HTML, ověřená doména.
- E-mail pro ověření adresy a obnovu hesla, SMS pro druhý faktor a situace mimo schránku.
Jak posílat ověřovací e-maily přes TopEmail, najdete v přehledu API a SMTP.
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 →