빠른 비교
import { createHash, createHmac, randomBytes, randomUUID } from "node:crypto";
const fileHash = createHash("sha256").update(fileBuffer).digest("hex");
const apiToken = randomBytes(32).toString("hex");
const requestId = randomUUID();
const signature = createHmac("sha256", secret).update(payload).digest("hex");해시와 랜덤
먼저 머릿속에 넣어둘 기본형은 아래입니다.
- 무결성/지문:
createHash("sha256") - 서명/검증용 비밀키 해시:
createHmac(...) - 랜덤 바이트 토큰:
randomBytes(size) - 식별자:
randomUUID() - 비밀번호 저장:
scrypt()또는pbkdf2()
해시는 "같은 입력 -> 같은 결과"가 필요할 때 쓴다
파일 무결성, 캐시 키, 콘텐츠 fingerprint 같은 용도면 해시 카드가 맞습니다.
랜덤은 "예측 불가능한 값"이 필요할 때 쓴다
세션 토큰, CSRF 값, 재설정 토큰은 해시가 아니라 랜덤이 핵심입니다.
비밀번호 저장은 단순 해시 카드가 아니다
이 카드에서 가장 중요한 경계는 "비밀번호 저장에 createHash('sha256')를 바로 쓰지 않는다"는 점입니다. 비밀번호는 scrypt, pbkdf2, argon2 계열로 가야 합니다.
HMAC은 해시와 비슷해 보여도 목적이 다르다
입력 지문을 만드는 해시와 달리, HMAC은 비밀키를 함께 써서 위변조 여부를 보는 카드입니다. Webhook 서명 검증, 내부 메시지 서명처럼 "누가 만들었는가"까지 보려면 HMAC 쪽이 맞습니다.
토큰과 식별자는 구분한다
randomUUID()는 추적용 ID나 요청 ID에 잘 맞고, randomBytes()는 비밀 토큰에 더 자연스럽습니다.
둘 다 랜덤처럼 보여도 보안 강도와 표현 목적이 다릅니다.
import { randomBytes, randomUUID } from "node:crypto";
const requestId = randomUUID(); // 로그/추적 ID
const resetToken = randomBytes(32).toString("hex"); // 비밀 토큰로그 상에서 사람 눈으로 식별할 값이면 randomUUID()가 읽기 쉽고, 외부에 노출되는 토큰이면 길이와 예측 불가능성이 더 중요한 randomBytes()가 맞습니다.
언제 무엇을 쓸까
체크포인트
- 파일 fingerprint:
createHash("sha256") - 비밀번호 저장:
scrypt()또는pbkdf2() - 랜덤 토큰:
randomBytes() - 식별자:
randomUUID() - 서명/검증:
createHmac() - 위변조 검증은 단순 해시가 아니라 HMAC 쪽을 먼저 본다
빠른 선택
- "같은 파일인지 확인" -> hash
- "이 요청이 우리 비밀키로 서명됐는가" -> HMAC
- "재설정 링크 토큰 만들기" -> randomBytes
- "로그에서 요청 추적" -> randomUUID
- "사용자 비밀번호 저장" -> scrypt / pbkdf2주의할 점
빠른 해시를 비밀번호 저장에 쓰면 안 된다. 무결성 확인과 비밀번호 저장은 같은 "해시"처럼 보여도 보안 목표가 다르다.
참고 링크
1 sources