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.
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
}