Menu
CoddyTech

비밀번호 확인하기

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진수 텍스트를 다시 바이트로 변환합니다.

challenge icon

챌린지

쉬움
  • 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')}`;
}

인증과 보안의 모든 레슨