비밀번호 확인하기
Coddy Node.js 여정의 인증과 보안 섹션에 포함된 레슨. 17개 중 2번째.
해시는 되돌릴 수 없으므로 로그인 과정에서 저장된 비밀번호를 다시 읽지 않습니다. 입력한 비밀번호도 같은 방식으로 해시하고 두 키를 비교합니다. 같은 방식이란 같은 솔트를 사용한다는 뜻입니다. 저장된 salt:hash를 분리해 솔트를 재사용하세요.
===가 아니라 timingSafeEqual로 비교하세요. ===는 다른 첫 바이트에서 비교를 멈추므로, 공격자는 여러 답변의 소요 시간을 측정해 추측한 값 중 얼마나 맞았는지 알아낼 수 있습니다. timingSafeEqual은 항상 같은 시간이 걸립니다. 길이가 같은 두 Buffer를 비교하며, 길이가 다르면 오류를 발생시킵니다.
import { timingSafeEqual } from 'node:crypto';
const expected = Buffer.from(process.env.API_KEY);
const given = Buffer.from(req.get('x-api-key') ?? '');
const ok = given.length === expected.length && timingSafeEqual(given, expected);Buffer.from(hex, 'hex')는 16진수 텍스트를 다시 바이트로 변환합니다.
챌린지
쉬움passwords.js에서verifyPassword(password, stored)를 추가하고 내보내세요. 비밀번호가 저장된 값을 만든 비밀번호라면 true를 반환합니다.server.js에서POST /login을 추가하세요.{ username, password }를 받아, 올바르지 않은 비밀번호이거나 알 수 없는 이름이면 401{ "error": "Wrong username or password" }를 반환하고, 그렇지 않으면 200{ username }을 반환합니다.
ada로 가입하고, 로그인한 다음, 잘못된 비밀번호로 로그인해 보세요.
직접 해보기
import { randomBytes, scrypt } from 'node:crypto';
import { promisify } from 'node:util';
const scryptAsync = promisify(scrypt);
// 비밀번호를 signup이 저장하는 텍스트로 변환합니다.
export async function hashPassword(password) {
const salt = randomBytes(16).toString('hex');
const key = await scryptAsync(password, salt, 64);
return `${salt}:${key.toString('hex')}`;
}