Verificar una contraseña
Parte de la sección Autenticación y seguridad del Journey de Node.js de Coddy. Lección 6 de 24.
Un inicio de sesión deriva una clave a partir de la contraseña introducida mediante la sal guardada y la longitud de la clave original. Una sal nueva produciría una clave diferente incluso con la contraseña correcta. La contraseña original nunca se descodifica a partir del almacenamiento.
// Se almacenó un código de invitación como salt.keyHex
const [salt, keyHex] = savedInvite.split('.');
const expected = Buffer.from(keyHex, 'hex');
const attempted = await scryptAsync(inviteCode, salt, 32);Compara las secuencias de bytes resultantes usando timingSafeEqual, después de confirmar que sus longitudes coinciden. Esto evita una salida anticipada dependiente del contenido que podría revelar información mediante la temporización. === de Buffer comprueba la identidad, y una comparación normal de cadenas no ofrece ninguna garantía de tiempo constante. Tu función de contraseña usa el delimitador de dos puntos del registro de contraseña y una clave de 64 bytes. La ruta POST /login que añadas usa el resultado para devolver 200 o 401.
Desafío
FácilEste paso abre el proyecto de API de notas privadas preparado para el curso, en lugar de los archivos de tu proyecto de Nunca almacenes contraseñas (incluido passwords.js). Tu trabajo anterior sigue en su propia lección. Lee los archivos proporcionados antes de editar passwords.js. Esta copia contiene funciones auxiliares para aplicar hash a las contraseñas y las rutas de depuración proporcionadas.
Ahora el registro guarda hashes con sal. Añade un inicio de sesión que compruebe una contraseña con lo que guardó el registro.
- En
passwords.js, añade y exporta una funciónasyncverifyPassword(password, stored).storedes un valor que guardó el registro, comof2bb...f065:c3f7...dbd2. Devuelvetruecuandopasswordsea la contraseña que lo generó yfalseen caso contrario:- Toma la sal de antes de los dos puntos.
- Aplica hash a
passwordcon esa sal para obtener una clave de 64 bytes, de la misma manera que lo hacehashPassword. - Decodifica en hexadecimal la clave guardada después de los dos puntos. Si las dos claves tienen longitudes distintas en bytes, devuelve
falsesin llamar atimingSafeEqual, como en Comparar bytes de clave. De lo contrario, devuelve el resultado de la comparación.
- En
server.js, importaverifyPasswordjunto ahashPassword. AñadePOST /login. Recibe el mismo cuerpo que el registro, por ejemplo{"username": "ada", "password": "correct horse"}.- Busca al usuario con ese nombre de usuario.
verifyPasswordes lenta y devuelve una promesa, así que espera a que termine. - Si el usuario existe y la contraseña es correcta, responde con el estado 200 y
{"username": "ada"}. - En caso contrario, responde con el estado 401 y
{"error": "Wrong username or password"}. 401 Unauthorized significa que la solicitud no demostró quién la envía. - Envía exactamente la misma respuesta 401 tanto si la contraseña es incorrecta como si nadie se registró con ese nombre de usuario. Así, el cuerpo de la respuesta nunca revela qué nombres de usuario existen.
- Busca al usuario con ese nombre de usuario.
Pruébalo: Pulsa Ejecutar. Envía POST /signup con {"username": "ada", "password": "correct horse"}: estado 201. Envía POST /login con el mismo cuerpo: estado 200 y {"username": "ada"}. Envía POST /login con {"username": "ada", "password": "wrong horse"}: estado 401 y {"error": "Wrong username or password"}. Envía POST /login con {"username": "nobody", "password": "correct horse"}: el mismo 401.
Pruébalo tú mismo
import { randomBytes, scrypt } from 'node:crypto';
import { promisify } from 'node:util';
const scryptAsync = promisify(scrypt);
// Convierte una contraseña en el texto que almacena signup.
export async function hashPassword(password) {
const salt = randomBytes(16).toString('hex');
const key = await scryptAsync(password, salt, 64);
return `${salt}:${key.toString('hex')}`;
}
Todas las lecciones de Autenticación y seguridad
1Contraseñas
Bytes y texto hexadecimalDerivar una clave de contraseñaUna sal nuevaNunca almacenes contraseñasComparar bytes de claveVerificar una contraseñaReglas de registroPractica por tu cuenta: Playground de Express