Menu
CoddyTech

Vérifier un mot de passe

Fait partie de la section Authentification et sécurité du Journey Node.js de Coddy. Leçon 6 sur 24.

Une connexion dérive une clé à partir du mot de passe tenté, en utilisant le sel enregistré et la longueur originale de la clé. Un nouveau sel produirait une clé différente, même pour le bon mot de passe. Le mot de passe d’origine n’est jamais décodé à partir du stockage.

// Un code d’invitation était stocké sous la forme salt.keyHex
const [salt, keyHex] = savedInvite.split('.');
const expected = Buffer.from(keyHex, 'hex');
const attempted = await scryptAsync(inviteCode, salt, 32);

Comparez les séquences d’octets obtenues à l’aide de timingSafeEqual, après avoir vérifié que leurs longueurs correspondent. Cela évite une sortie anticipée dépendante du contenu, qui pourrait révéler des informations par le biais du minutage. === sur des Buffer vérifie l’identité, et une comparaison de chaînes classique ne garantit pas un temps constant. Votre fonction de mot de passe utilise le délimiteur deux-points de l’enregistrement du mot de passe et une clé de 64 octets. La route POST /login que vous ajoutez utilise le résultat pour renvoyer 200 ou 401.

challenge icon

Défi

Facile

Cette étape ouvre le projet d’API de notes privées préparé pour le cours, plutôt que les fichiers de ton projet de Ne stocke jamais les mots de passe (y compris passwords.js). Ton travail précédent reste dans sa propre leçon. Lis les fichiers fournis avant de modifier passwords.js. Cette copie contient des fonctions d’aide au hachage des mots de passe et les routes de débogage fournies.

L’inscription enregistre maintenant des hachages salés. Ajoute une connexion qui vérifie un mot de passe par rapport à celui enregistré lors de l’inscription.

  1. Dans passwords.js, ajoute et exporte une fonction async verifyPassword(password, stored). stored est une valeur enregistrée lors de l’inscription, comme f2bb...f065:c3f7...dbd2. Renvoie true si password est le mot de passe qui l’a produite, et false sinon :
    • Récupère le sel situé avant les deux-points.
    • Hache password avec ce sel pour obtenir une clé de 64 octets, de la même manière que hashPassword.
    • Décode en hexadécimal la clé enregistrée après les deux-points. Si les deux clés ont des longueurs différentes en octets, renvoie false sans appeler timingSafeEqual, comme dans Comparer les octets des clés. Sinon, renvoie le résultat de la comparaison.
  2. Dans server.js, importe verifyPassword à côté de hashPassword. Ajoute POST /login. Cette route reçoit le même corps de requête que celui de l’inscription, par exemple {"username": "ada", "password": "correct horse"}.
    • Recherche l’utilisateur avec ce nom d’utilisateur. verifyPassword est lent et renvoie une promesse, donc attends son résultat.
    • Lorsque l’utilisateur existe et que le mot de passe est correct, réponds avec le statut 200 et {"username": "ada"}.
    • Sinon, réponds avec le statut 401 et {"error": "Wrong username or password"}. 401 Unauthorized signifie que la requête n’a pas prouvé l’identité de la personne qui en est à l’origine.
    • Renvoie exactement le même statut 401 pour un mot de passe incorrect et pour un nom d’utilisateur qui n’a jamais été utilisé pour s’inscrire. Ainsi, le corps de la réponse ne révèle jamais à personne quels noms d’utilisateur existent.

Essaie : appuie sur « Jouer ». Envoie POST /signup avec {"username": "ada", "password": "correct horse"} : statut 201. Envoie POST /login avec le même corps : statut 200 et {"username": "ada"}. Envoie POST /login avec {"username": "ada", "password": "wrong horse"} : statut 401 et {"error": "Wrong username or password"}. Envoie POST /login avec {"username": "nobody", "password": "correct horse"} : le même statut 401.

Essayez vous-même

import { randomBytes, scrypt } from 'node:crypto';
import { promisify } from 'node:util';

const scryptAsync = promisify(scrypt);

// Transforme un mot de passe en texte stocké par signup.
export async function hashPassword(password) {
    const salt = randomBytes(16).toString('hex');
    const key = await scryptAsync(password, salt, 64);
    return `${salt}:${key.toString('hex')}`;
}

Toutes les leçons de Authentification et sécurité

Entraînez-vous par vous-même : Playground Express