Hashowanie haseł

Żeby zapewnić bezpieczeństwo naszym użytkownikom, nigdy nie przechowujemy ich haseł w czytelnej postaci, tylko przechowujemy ich odcisk (tak zwany hash). Z hasha nie da się odtworzyć pierwotnego hasła. Ważne jest użycie bezpiecznego algorytmu do jego utworzenia. Pomaga nam w tym klasa Nette\Security\Passwords.

Instalacja i wymagania

Framework automatycznie dodaje do kontenera DI usługę typu Nette\Security\Passwords pod nazwą security.passwords. Uzyskasz ją, pozwalając sobie ją przekazać przez wstrzykiwanie zależności.

use Nette\Security\Passwords;

class Foo
{
	public function __construct(
		private Passwords $passwords,
	) {
	}
}

__construct (string $algo=PASSWORD_DEFAULT, array $options=[])

Wybieramy, którego bezpiecznego algorytmu użyć do wygenerowania hasha, i konfigurujemy jego parametry.

Domyślnie używany jest PASSWORD_DEFAULT, czyli wybór algorytmu pozostawiony jest PHP. Algorytm może się zmienić w nowszych wersjach PHP, jeśli pojawią się nowsze, silniejsze algorytmy hashujące. Dlatego powinieneś mieć świadomość, że długość wynikowego hasha może się zmienić, i przechowywać go w sposób pozwalający pomieścić dostatecznie dużo znaków; zalecana szerokość to 255.

Przykład ustawienia szybkości hashowania dla algorytmu bcrypt przez zmianę parametru cost: (w 2020 domyślną wartością jest 10, hashowanie hasła zajmuje mniej więcej 80 ms; dla cost 11 to ok. 160 ms, dla cost 12 ok. 320 ms; im wolniej, tym lepsza ochrona, przy czym szybkość 10–12 jest już uznawana za wystarczającą ochronę)

// hasła będziemy hashować 2^12 (2^cost) iteracjami algorytmu bcrypt
$passwords = new Passwords(PASSWORD_BCRYPT, ['cost' => 12]);

Przy użyciu wstrzykiwania zależności:

services:
	security.passwords: Nette\Security\Passwords(::PASSWORD_BCRYPT, [cost: 12])

static bcrypt (?int $cost=null): Passwords

Tworzy instancję skonfigurowaną dla algorytmu bcrypt. Parametr $cost ustawia opisaną wyżej szybkość hashowania; jeśli go pominiesz, użyta zostanie wartość domyślna PHP.

$passwords = Passwords::bcrypt(12);

static argon2id (?int $memoryCost=null, ?int $timeCost=null, ?int $threads=null): Passwords

Tworzy instancję skonfigurowaną dla algorytmu Argon2id. Pominięte parametry pozostają na wartościach domyślnych PHP. Jeśli PHP zostało zbudowane bez wsparcia dla Argon2, metoda rzuca Nette\NotSupportedException.

$passwords = Passwords::argon2id(memoryCost: 1 << 17, timeCost: 4);

hash (string $password): string

Generuje hash hasła.

$res = $passwords->hash($password); // Zahashuje hasło

Wynik $res to ciąg, który oprócz samego hasha zawiera identyfikator użytego algorytmu, jego ustawienia i sól kryptograficzną (losowe dane zapewniające, że dla tego samego hasła powstanie inny hash). Jest więc wstecznie kompatybilny; jeśli na przykład zmienisz parametry, hashe zapisane przy poprzednich ustawieniach da się nadal zweryfikować. Cały ten wynik zapisujemy do bazy danych, więc nie trzeba osobno przechowywać soli ani ustawień.

verify (string $password, string $hash)bool

Ustala, czy podane hasło odpowiada podanemu hashowi. $hash uzyskaj z bazy danych według wpisanej nazwy użytkownika albo adresu e-mail.

if ($passwords->verify($password, $hash)) {
	// poprawne hasło
}

needsRehash (string $hash)bool

Ustala, czy hash odpowiada opcjom podanym w konstruktorze.

Przydaje się to na przykład wtedy, gdy zmieniasz cost hashowania. Weryfikacja odbywa się według zapisanych ustawień, a jeśli needsRehash() zwróci true, trzeba utworzyć hash na nowo, tym razem z nowymi parametrami, i ponownie zapisać go w bazie danych. Automatycznie “aktualizuje” to zapisane hashe przy logowaniu użytkowników.

if ($passwords->needsRehash($hash)) {
	$hash = $passwords->hash($password);
	// zapisz $hash do bazy danych
}
wersja: 4.x