Bezpečné přihlašování v PHP
1. 2. 2026 | Ing. Tomáš Hojgr, MBA, LL.M. | majitel
PHP má moc hezkou sadu funkcí password_hash(), password_verify(), password_needs_rehash(). Tyto funkce jsou velmi bezpečné a navíc jsou velmi jednoduché na použití. Přesto mnoho vývojářů stále používá svoje proprietární konstrukce.
Jak uložit heslo uživatele velmi bezpečně
V kroku jedna mám heslo v čitelné podobě, protože si jej uživatel právě zvolil, anebo jsem mu jej vygeneroval. Nikam to heslo neukládám, jen ho proženu funkcí password_hash($uzivatelemZadaneHeslo). A výsledek uložím do databáze.
Jak porovnat zadané heslo s hashem v databázi
Až se uživatel přihlašuje, zadá mi opět heslo. Já jej předám spolu s tím hashem hesla, které mám uložené v databázi, funkci password_verify($uzivatelemZadaneHeslo, $hashovanaVerzeHeslaZDatabaze). Oproti staršímu přístupu se nesnažím heslo nejdřív zahashovat a pak porovnávat otisky.
Jak upgradovat algoritmus hesla bez obtěžování uživatele
Pokud se uživatel právě úspěšně přihlásil, pak mám v tu chvíli jeho heslo k dispozici. V $_POST; A můžu ověřit, jestli je stále bezpečně uloženo, anebo už jsou požadavky na bezpečnost hesla vyšší.
Pomůže mi s tím funkce password_needs_rehash($hashovanaVerzeHeslaZDatabaze, $vyzadovanyAlgoritmus, $options); A pokud ta řekne, že požadavky na heslo již neodpovídají, pak mám stále právě teď k dispozici uživatelem zadané heslo v čitelné podobě. Takže vygeneruju jeho hash, který aktuálním požadavkům odpovídá, a v databázi jej nahradím touto verzí. Nyní už je heslo opět bezpečné.
Jak resetovat heslo
Kdy se resetuje heslo:
- Pokud si uživatel přeje heslo změnit. Např. z důvodu prevence, nebo ví, že bylo kompromitováno.
- Pokud uživatel heslo zapomněl. Jen už není vhodné to tak nazývat, protože pojem zapomenuté heslo zdůrazňuje, že uživatel udělal chybu a heslo zapomněl. Nikdo ale nechce svému uživateli říkat, že udělal chybu, cítil by se špatně.
- Z nějakého důvodu se nemůže přihlásit. Na vině může být i technika, systém necitlivě změnil algoritmus hesel, nebo promazal uniklá hesla.
A není dobré mít více mechanismů resetování hesel, protože je to více mechanismů, které je potřeba udržovat, lze je zneužít, a jsou to mechanismy vedoucí ke stejnému cíli, tak proč na to mít více cest.
Všem těm scénářům nejvíce vyhovuje odkaz na reset hesla.
UX postup resetu hesla
V osobním profilu a na stránce pro přihlašování je potřeba mít odkaz "Resetovat heslo".
UX TIP: Pokud je ten odkaz na stránce pro přihlašování, a uživatel už má vyplněno přihlašovací jméno, je vhodné jej použít, aby už bylo vyplněno na stránce pro resetování hesla.
Kliknutím na ten odkaz se uživatel dostane na vyhrazenou stránku, na které zadá přihlašovací jméno. A pozor, pokud je zadáno chybně, není vhodné o tom informovat. Útočník by takovou informaci mohl zneužít při hledání userů, kteří ne/existují. Proto je vhodné použít nic neříkající hlášku, která popisuje že technicky vše proběhlo ok a výsledek lze očekávat. Např.:
Na email zadaného uživatele byl odeslán odkaz pro resetování hesla, pokud zadaný uživatel existuje. Odkaz je platný po dobu 10 minut.
Je fajn informovat i o tom, že platnost toho odkazu je omezená.
Proč mít platnost odkazu omezenou?
Pokud by email s odkazem někdy nějak otekl, mohl by být zneužit. Např. email omylem přepošlete, uteče přes historii v browseru, přes schánku copy&paste.
Jak to řešit technicky
Po požadavku na reset hesla se vytvoří nový řádek v tabulce s takovými požadavky. Uloží se IP, browser a všechny možné další údaje, které jsou v té chvíli k dispozici.
Ten řádek bude mít unikátní hash, který bude součástí odkazu.
Ten odkaz se pošle uživateli mailem. Spolu s instrukcemi k jeho použití a informací, do kdy platí. Pozor na časová pásma.
Generování nového hesla nedělejte vy - (heslo poslané emailem je kompromitované by design)
Nabídněte uživateli pomoc např. JS odkazem, který heslo jen vygeneruje, aniž byste jej někde ukládali, nebo někam posílali. Určitě to neřešte AJAXem, je zbytečné aby se to heslo toulalo po síti zbytečně.
Ať už si heslo naťuká, vloží, nebo použije váš JS generátor, heslo prožeňte password_hash() a uložte otisk.
Do tabulky s těmi požadavky si uložte kdo, kdy, z jaké IP, browseru atp. ten odkaz otevřel. Budete mít data, kdyby to byl podvod, a nastavením data použití ten odkaz taky zneplatníte.
Upozorněte uživatele na změnu hesla, emailem
Když reset hesla projde v pořádku, pošlete tu informaci emailem uživateli. Pokud má email kompromitovaný a útočník by si ten email mohl smazat, je už uživatel stejně v pytli a moc mu nemáte jak pomoci.
Ale pokud mu někdo změní heslo, a nebyl to on, dáte mu šanci reagovat "Haloó, to jsem nebyl já, hned to nějak řešme." Může být fajn do takového emailu přidat odkaz na okamžitou blokaci účtu.
Limitujte počet požadavků na změnu hesla v čase
Zablokujete tím útočníky, kteří se mohou usnažit opakovaně.
Evidujte zařízení uživatele a vyžádejte si potvrzení souhlasu pro nová zařízení
IP adresa se může měnit v rámci připojení, např. přes mobil, a nic to nemusí znamenat.
Ale pokud se uživateli v průběhu relace změní označení zařízení, a to nové je pro vás neznámé, řešte to.
Účet dočasně blokněte, uživateli řekněte co se děje, proč, a jak má pokračovat. Emailem mu pošlete odkaz k odblokování, resp. schválení nového zařízení. A uložte si jej, příště už pro vás bude známé.
Tento postup brání např. ukradení session.
Závěr
Buďte paranoidní, vždy jsou postupy zneužití, které zatím neznáte.
Ale porovnávejte co je nutné minimum, a kdy už by to uživatele obtěžovalo - vezme v potaz co chráníte.
Jiné požadavky na bezpečnost bude mít seznam provedených objenávek uživatele v eshopu se zubními pastami, a jiné přístup do bankovnictví, nebo možnosti tvořit závazné objednávky ve velkoobchodním systému.