데이터 암호화은(는) 무엇을 하나요?
규칙
- 모든 곳에 TLS 적용: HTTP를 HTTPS로 리디렉션, HSTS 헤더 설정, 암호화되지 않은 연결 절대 허용 안 함
- PII(개인 식별 정보) 칼럼은 데이터베이스에 저장하기 전에 애플리케이션 수준에서 AES-256-GCM으로 암호화
- 이메일은 조회용으로 SHA-256 해싱, 표시용으로 AES-256-GCM 암호화 — 데이터 노출 없이 검색 가능
- 비밀번호: bcrypt 또는 argon2id만 사용 — SHA, MD5 또는 복호화 가능한 암호화 절대 사용 금지
- 데이터베이스 백업 암호화 및 암호화 키는 데이터와 별도로 저장
- 브라우저 측 암호화는
crypto.subtle 사용, 서버 측은 node:crypto 사용
- 암호화 키는 시크릿 매니저에 저장, 코드나 암호화된 데이터와 함께 절대 저장 금지
import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
const ENCRYPTION_KEY = Buffer.from(process.env.ENCRYPTION_KEY!, "hex");
function encrypt(plaintext: string): string {
const iv = randomBytes(12);
const cipher = createCipheriv("aes-256-gcm", ENCRYPTION_KEY, iv);
const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
const tag = cipher.getAuthTag();
return Buffer.concat([iv, tag, encrypted]).toString("base64");
}
function decrypt(ciphertext: string): string {
const buf = Buffer.from(ciphertext, "base64");
const iv = buf.subarray(0, 12);
const tag = buf.subarray(12, 28);
const encrypted = buf.subarray(28);
const decipher = createDecipheriv("aes-256-gcm", ENCRYPTION_KEY, iv);
decipher.setAuthTag(tag);
return decipher.update(encrypted) + decipher.final("utf8");
}
피해야 할 사항
- PII를 평문으로 저장 — 데이터베이스 침해 시 모든 정보 노출
- ECB 모드 또는 인증 없는 AES(CBC with HMAC 없음) 사용 — GCM 사용
- MD5 또는 SHA로 비밀번호 해싱 — 이들은 비밀번호 해싱 알고리즘이 아님
- 암호화 키를 소스 코드에 하드코딩 — 환경 변수 또는 KMS 사용
- 내부 통신이라도 HTTP를 통해 민감 데이터 전송 — 모든 연결에 TLS 사용