Node.js를 독학하면서 웹사이트 구조를 하나씩 따라 만들어보다가, 자연스럽게 회원가입 → 로그인 → 권한 부여 과정을 접하게 됐다.
근데 문득 이런 생각이 들었다..
"회원가입할 때 입력한 비밀번호는 DB에 어떻게 저장되지?"
당연히 암호화는 하겠지 싶었지만, 구체적으로 어떤 방식으로 저장되는지는 잘 몰랐다.
그래서 이번 기회에 비밀번호 보안과 관련된 개념을 정리해봤다.
비밀번호를 평문으로 저장하면 왜 안 될까?
- 개인정보보호법 위반
- DB가 해킹당하면 해커가 바로 로그인 가능
- 대부분 사람들이 사이트마다 같은 비밀번호를 쓰기 때문에 다른 사이트들도 줄줄이 털릴 수 있음
암호화와 해싱의 차이
보통 "암호화"라는 단어를 많이 쓰는데, 실제로는 해싱(Hashing)이라는 걸 사용한다.
둘은 비슷해 보여도 큰 차이점이 하나 있다. 바로 복호화 가능 여부이다.
구분 | 복호화 가능? | 설명 |
암호화 (Encryption) | O | 양방향. 암호화 → 복호화 가능 |
해싱 (Hashing) | ❌ | 단방향. 한 번 해싱하면 원래 값으로 못 돌림 |
그래서 비밀번호는 해싱을 사용해 저장해야 한다.
복호화 자체가 안 되는 단방향이기 때문에 해커가 털어도 원본 비밀번호는 못 알아낸다.
단방향 해시 함수란?
단방향 해시 함수는 어떤 문자열(예: 비밀번호)을 입력하면 고정된 길이의 해시값(digest)을 뱉어내는 수학적 알고리즘이다.
대표적인 해시 알고리즘은 다음과 같다
- SHA (보통 SHA-256)
- MD5
- WHIRLPOOL
결론적으로,
단방향 해싱을 사용하여 복호화가 되지 않게 하여 DB에 저장해야합니다.
단방향 해시 함수(One-Way Hash Funtion)
단방향 해시 함수는 수학적 알고리즘으로 평문 암호를 매핑하여 암호화된 데이터로 변환하는 것을 말한다.
변환하는 것을 해시라고 하는데, 해시로 암호화된 데이터는 다이제스트(digest)라고 합니다다.
단방향 해시 함수 알고리즘에 종류는 많지만 대표적으로 아래와 같은 것 들이 있다.
- SHA
- MD
- WHIRLPOOL
- HAS
예를 들어, 비밀번호 123456을 SHA-256으로 해싱하면 xxxxxxx 같은 다이제스트가 나온다.
이걸 DB에 저장해놓고, 로그인할 때 같은 방식으로 해싱한 값이 일치하는지만 확인하는 방식이다.
이론상으론 복호화가 불가능하니, 어느 정도 보안 효과는 있음.
단방향 해싱의 한계
그런데 이 해싱이라는 것도 만능은 아니다. 몇 가지 취약점이 있다.
1. 동일한 입력값 → 동일한 다이제스트
- 예: 1234를 해싱하면 매번 같은 값이 나옴
- 그래서 미리 다이제스트를 모아놓은 레인보우 테이블(Rainbow Table)이 존재함
2. Brute Force 공격
- 모든 가능한 조합을 일일이 다 해보는 무식한 공격 방식
3. 해시 충돌 (Hash Collision)
- 서로 다른 값인데, 해싱 결과가 같은 경우
보완 방법들
1. 키 스트레칭 (Key Stretching)
- 해싱을 여러 번 반복해서 다이제스트를 만드는 방식이다.
예를 들어 SHA-256으로 한 번만 해싱하는 게 아니라, 수십~수천 번 반복해서 더 복잡한 다이제스트를 만들어낸다.
문제점:
- 해커가 반복 횟수(N)를 알아내면 똑같이 레인보우 테이블을 만들 수도 있고
- 몇 번 해싱했는지 기억해야 하는 불편함이 있음
그래서 키 스트레칭만으로는 부족하다.
2. 솔트(Salt)
- 해싱 전에 평문 비밀번호에 랜덤 문자열을 덧붙이는 방식이다. 이걸 소금 친다고 해서 Salt라고 부른다.
예를 들어
- 평문 비밀번호: 1234
- 솔트: !@#zxc
- 결합한 문자열: 1234!@#zxc
- 최종 해싱: 8f3b1a... ← 완전 다른 다이제스트
이렇게 하면 같은 비밀번호라도 전혀 다른 해시값을 가지게 되어, 레인보우 테이블이 무력화된다.
솔트는 일반적으로 함께 저장되며, 각 사용자마다 다르게 적용됩니다.
SHA-256 같은 일반 해시 함수보다는 bcrypt, scrypt, argon2 같은 보안성이 검증된 해시 라이브러리를 사용하는 것이 일반적이다.
왜냐하면 솔트와 키 스트레칭을 자동으로 처리해주기 때문에, 직접 구현할 필요 없이 안정적인 방식으로 사용할 수 있다.
따라서 Node.js에서는 bcrypt를 가장 많이 사용한다.
정리하면
비밀번호는 단순히 암호화한다고 끝나는 게 아니라, 단방향 해싱과 함께 여러 보완 기법을 적용해야 한다.
특히 실무에서는 단순 SHA-256만 돌리는 방식은 보안상 취약하므로 반드시 전용 라이브러리를 사용할 것을 권장해야한다.