
crypto는 Node.js에서 제공하는 내장(표준) 암호화 모듈이다.
암호화 방식에따라 양방향, 단방향 암호화가 가능하다.
대표적인 양방향 방식으로는 아래와 같은 항목이 있다.
양방향 암호화는 원문을 다시 복원해야 하는 데이터 전송 시 사용된다.
예를 들어, 결제 정보와 민감한 개인정보 등이 있다.
단방향 암호화라고 부르는 영역은 실제로는 해시 기반 방식이며,
비밀번호 저장에서는 PBKDF2 같은 KDF 계열을 사용한다.
해시는 입력 값으로 고정 길이 출력을 만드는 함수이고,
출력으로 입력을 복구(복호화)할 수 없다.
양방향 암호화 방식은 키가 있으면 복호화가 가능하고,
단방향 암호화는 복호화 개념이 없음을 알 수 있다.
해시는 데이터 무결성 검증이나 비밀번호 저장 등에 사용된다.
이 중 웹 서비스에서 가장 중요한 사용처는 비밀번호 저장이다.
비밀번호를 안전하게 저장하기 위해 해시를 쓰는 이유는,
서버가 비밀번호를 다시 알아낼 필요가 없기 때문이다.
서비스에 가입할 때 사용자가 입력한 비밀번호를 그대로 DB에 저장하면,
DB가 유출되는 순간 모든 계정이 즉시 위험해진다.
그래서 비밀번호는 아래 원칙을 따른다.
const crypto = require('crypto');
const password = '1111';
const salt = crypto.randomBytes(64).toString('base64');
const hashPassword = crypto.pbkdf2Sync(password, salt, 10000, 64, 'sha512').toString('base64');
crypto 모듈을 불러와서 PBKDF2 메서드를 사용한다.
1) password + salt를 기반으로
2) 지정한 반복 횟수(iterations)만큼 연산을 반복하고
3) 최종적으로 일정 길이의 키(derived key)를 만들어낸다.
4) 이 derived key를 비밀번호 해시 값처럼 DB에 저장한다.
crypto.pbkdf2Sync(password, salt, iterations, keylen, digest)
password: 사용자가 입력한 비밀번호(원문)salt: 랜덤 바이트(사용자별로 다르게)iterations: 반복 횟수 (클수록 느려지고 안전해짐)keylen: 결과로 만들 키 길이(바이트)digest: 내부 HMAC에 사용할 해시 알고리즘(예: sha512)salt는 같은 비밀번호라도 사용자마다 다른 해시 결과가 나오게 만들기 위한 랜덤 값이다.
해커들이 비밀번호를 공격할 때는 보통 아래와 같은 방식을 사용한다.
만약 salt 없이 단순히 해시 형태로만 저장한다면?
1) 같은 비밀번호면 항상 같은 해시가 나온다
'1234'를 쓰는 사람이 많으면 DB 안에 동일한 해시 값이 반복된다.
2) 사전 공격이 매우 쉬워진다
이미 계산된 해시 목록과 바로 비교할 수 있기 때문이다.
그래서 각 사용자마다 랜덤한 salt를 생성해
비밀번호와 함께 PBKDF2 내부 로직(HMAC 기반)으로 처리한다.
이렇게 하면 같은 비밀번호라도 사용자마다 서로 다른 해시가 생성되고,
공격자는 사용자별로 다시 계산해야 하므로 비용이 급격히 증가한다.
즉 salt의 목적은 아래와 같이 정리할 수 있다.