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
}