SHIN
Node.js 실전 팁 20선
20편Node.js 내장 crypto 모듈로 대부분의 암호화 작업을 외부 의존성 없이 처리할 수 있습니다.
import { scrypt, randomBytes, timingSafeEqual } from 'crypto';
import { promisify } from 'util';
const scryptAsync = promisify(scrypt);
async function hashPassword(password) {
const salt = randomBytes(16).toString('hex');
const derivedKey = await scryptAsync(password, salt, 64);
return `${salt}:${derivedKey.toString('hex')}`;
}
async function verifyPassword(stored, input) {
const [salt, hash] = stored.split(':');
const derivedKey = await scryptAsync(input, salt, 64);
const storedKey = Buffer.from(hash, 'hex');
// timingSafeEqual: 타이밍 공격 방지
return timingSafeEqual(storedKey, derivedKey);
}
const hashed = await hashPassword('myPassword123');
await verifyPassword(hashed, 'myPassword123'); // trueimport { createHash } from 'crypto';
function sha256(data) {
return createHash('sha256').update(data).digest('hex');
}
// 파일 무결성 검증
import { createReadStream } from 'fs';
async function fileHash(filePath) {
const hash = createHash('sha256');
const stream = createReadStream(filePath);
for await (const chunk of stream) hash.update(chunk);
return hash.digest('hex');
}import { createHmac } from 'crypto';
function signPayload(payload, secret) {
return createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex');
}
function verifyPayload(payload, signature, secret) {
const expected = signPayload(payload, secret);
// 타이밍 공격 방지
return timingSafeEqual(
Buffer.from(expected, 'hex'),
Buffer.from(signature, 'hex')
);
}import { createCipheriv, createDecipheriv, randomBytes } from 'crypto';
const ALGO = 'aes-256-gcm';
function encrypt(text, key) {
const iv = randomBytes(12);
const cipher = createCipheriv(ALGO, key, iv);
const encrypted = Buffer.concat([
cipher.update(text, 'utf8'),
cipher.final(),
]);
const tag = cipher.getAuthTag();
return {
iv: iv.toString('hex'),
data: encrypted.toString('hex'),
tag: tag.toString('hex'),
};
}
function decrypt({ iv, data, tag }, key) {
const decipher = createDecipheriv(ALGO, key, Buffer.from(iv, 'hex'));
decipher.setAuthTag(Buffer.from(tag, 'hex'));
return Buffer.concat([
decipher.update(Buffer.from(data, 'hex')),
decipher.final(),
]).toString('utf8');
}
// 키는 32바이트(256비트) 랜덤 생성
const key = randomBytes(32);
const encrypted = encrypt('민감한 데이터', key);
const decrypted = decrypt(encrypted, key);import { randomUUID, randomBytes } from 'crypto';
// UUID v4
const id = randomUUID(); // 'a1b2c3d4-e5f6-...'
// 안전한 랜덤 토큰 (세션, API 키 등)
const token = randomBytes(32).toString('base64url'); // URL-safe주의: MD5, SHA-1은 비밀번호 해싱에 사용하지 마세요. 비밀번호에는 반드시
scrypt,bcrypt,argon2처럼 느린 알고리즘을 사용하세요.