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.

Instalace a požadavky

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
}
verze: 4.0 3.x 2.x