Hashování hesel
Abychom zajistili bezpečnost našich uživatelů, neukládáme jejich hesla v čitelné podobě, ale uložíme si pouze otisk (tzv. hash). Z otisku nelze zpětně zrekonstruovat původní podobu hesla. Je důležité použít bezpečný algoritmus, kterým otisk vytvoříme. S tím nám pomůže třída Nette\Security\Passwords.
Framework automaticky přidává do DI kontejneru službu typu Nette\Security\Passwords
pod názevem
security.passwords
, ke které se dostanete tak, že si ji necháte předat pomocí dependency injection.
use Nette\Security\Passwords;
class Foo
{
public function __construct(
private Passwords $passwords,
) {
}
}
__construct($algo=PASSWORD_DEFAULT, array $options=[]): string
Volíme, který bezpečný algoritmus pro generování hashe použít a konfigurujeme jeho parametry.
Jako výchozí se používá PASSWORD_DEFAULT
, tedy volba algoritmu se nechává na PHP. Algoritmus se může
v novějších verzích PHP změnit, pokud se objeví novější, silnější hashovací algoritmy. Proto byste si měli být
vědomi, že délka výsledného hashe se může změnit, a měli byste jej uložit způsobem, který dokáže pojmout dostatek
znaků, 255 je doporučená šířka.
Příklad nastavení rychlosti hashování algoritmem bcrypt změnou parametru cost: (v roce 2020 je výchozí 10, hashování hesla trvá zhruba 80ms, pro cost 11 je to cca 160ms, pro cost 12 zhruba 320ms, čím pomalejší, tím lepší ochrana, přičemž rychlost 10–12 se již považuje za ochranu dostatečnou)
// budeme hesla hashovat 2^12 (2^cost) iteracemi algoritmu bcrypt
$passwords = new Passwords(PASSWORD_BCRYPT, ['cost' => 12]);
Pomocí dependency injection:
services:
security.passwords: Nette\Security\Passwords(::PASSWORD_BCRYPT, [cost: 12])
hash(string $passwords): string
Vygeneruje hash hesla.
$res = $passwords->hash($password); // Zahashuje heslo
Výsledek $res
je řetězec, který kromě samotného hashe obsahuje i identifikátor použitého algoritmu, jeho
nastavení a kryptografickou sůl (náhodná data zajišťující, že pro stejné heslo se vygeneruje jiný hash). Je tedy
zpětně kompatibilní, když například změníte parametry, tak i hashe uložené s použitím předchozího nastavení
půjdou ověřit. Celý tento výsledek se ukládá do databáze, takže není potřeba ukládat sůl nebo nastavení
zvlášť.
verify(string $password, string $hash): bool
Zjistí, zda dané heslo odpovídá danému otisku. $hash
získejte z databáze podle zadaného uživatelského
jména nebo e-mailové adresy.
if ($passwords->verify($password, $hash)) {
// správné heslo
}
needsRehash(string $hash): bool
Zjistí, zda hash odpovídá v konstruktoru zadaným volbám.
Hodí se použít ve chvíli, kdy např. měníte rychlost hashování. Ověření proběhne podle uloženého nastavení a
pokud needsRehash()
vrátí true
, tak je potřeba znovu vytvořit hash, tentokrát s novými parametry,
a uložit ho znovu do databáze. Takto se automaticky „upgradují“ uložené hashe při přihlašování uživatelů.
if ($passwords->needsRehash($hash)) {
$hash = $passwords->hash($password);
// uložit $hash do databáze
}