Sprawdzanie hasła
Część sekcji Uwierzytelnianie i bezpieczeństwo ścieżki Node.js w Coddy. Lekcja 6 z 24.
Podczas logowania na podstawie wprowadzonego hasła wyprowadzany jest klucz przy użyciu zapisanego soli i oryginalnej długości klucza. Nowa sól dałaby inny klucz, nawet dla prawidłowego hasła. Oryginalne hasło nigdy nie jest odczytywane z pamięci.
// Kod zaproszenia zapisano w postaci salt.keyHex
const [salt, keyHex] = savedInvite.split('.');
const expected = Buffer.from(keyHex, 'hex');
const attempted = await scryptAsync(inviteCode, salt, 32);Porównaj otrzymane sekwencje bajtów za pomocą timingSafeEqual, po potwierdzeniu, że mają tę samą długość. Zapobiega to zależnemu od zawartości wcześniejszemu zakończeniu działania, które mogłoby ujawnić informacje przez pomiar czasu. Operator === dla obiektów Buffer sprawdza tożsamość, a zwykłe porównanie ciągów znaków nie gwarantuje stałego czasu wykonania. Twoja funkcja hasła używa dwukropka jako separatora w rekordzie hasła oraz klucza o długości 64 bajtów. Dodana przez Ciebie trasa POST /login używa wyniku, aby zwrócić kod 200 lub 401.
Wyzwanie
ŁatwyTen krok otwiera przygotowany projekt API prywatnych notatek kursu, a nie pliki Twojego projektu z lekcji Nigdy nie przechowuj haseł (w tym passwords.js). Twoja wcześniejsza praca pozostaje w swojej własnej lekcji. Przeczytaj dostarczone pliki przed edycją passwords.js. Ta kopia zawiera funkcje pomocnicze do haszowania haseł oraz dostarczone trasy debugowania.
Rejestracja zapisuje teraz hasze z solą. Dodaj logowanie, które sprawdza hasło względem tego, co zapisała rejestracja.
- W pliku
passwords.jsdodaj i eksportuj funkcjęasyncverifyPassword(password, stored).storedto wartość zapisana podczas rejestracji, na przykładf2bb...f065:c3f7...dbd2. Zwróćtrue, gdypasswordjest hasłem, które ją utworzyło, a w przeciwnym raziefalse:- Pobierz sól znajdującą się przed dwukropkiem.
- Przekształć
passwordprzy użyciu tej soli w klucz o długości 64 bajtów, tak samo jak robi tohashPassword. - Odkoduj zap zapisany klucz znajdujący się po dwukropku z formatu szesnastkowego. Jeśli długości kluczy w bajtach są różne, zwróć
falsebez wywoływaniatimingSafeEqual, tak jak w lekcji „Porównywanie bajtów klucza”. W przeciwnym razie zwróć wynik porównania.
- W pliku
server.jszaimportujverifyPasswordobokhashPassword. DodajPOST /login. Otrzymuje ono takie samo ciało żądania jak rejestracja, na przykład{"username": "ada", "password": "correct horse"}.- Znajdź użytkownika o podanej nazwie użytkownika.
verifyPassworddziała wolno i zwraca obietnicę, więc poczekaj na jej rozwiązanie. - Jeśli użytkownik istnieje, a hasło jest poprawne, odpowiedz statusem 200 i treścią
{"username": "ada"}. - W przeciwnym razie odpowiedz statusem 401 i treścią
{"error": "Wrong username or password"}. 401 Unauthorized oznacza, że żądanie nie potwierdziło, kto je wysyła. - Wysyłaj dokładnie taką samą odpowiedź 401 w przypadku błędnego hasła i nazwy użytkownika, dla której nikt się nie zarejestrował. Dzięki temu treść odpowiedzi nigdy nie ujawni nikomu, które nazwy użytkowników istnieją.
- Znajdź użytkownika o podanej nazwie użytkownika.
Wypróbuj: Naciśnij „Graj”. Wyślij POST /signup z treścią {"username": "ada", "password": "correct horse"}: status 201. Wyślij POST /login z tą samą treścią: status 200 i {"username": "ada"}. Wyślij POST /login z treścią {"username": "ada", "password": "wrong horse"}: status 401 i {"error": "Wrong username or password"}. Wyślij POST /login z treścią {"username": "nobody", "password": "correct horse"}: ten sam status 401.
Spróbuj swoich sił
import { randomBytes, scrypt } from 'node:crypto';
import { promisify } from 'node:util';
const scryptAsync = promisify(scrypt);
// Przekształca hasło w tekst przechowywany przez signup.
export async function hashPassword(password) {
const salt = randomBytes(16).toString('hex');
const key = await scryptAsync(password, salt, 64);
return `${salt}:${key.toString('hex')}`;
}
Wszystkie lekcje w sekcji Uwierzytelnianie i bezpieczeństwo
Poćwicz samodzielnie: Edytor online Express