본문으로 건너뛰기
>_ITDITD웹 보안 플랫폼

보안 가이드

비밀번호를 안전하게 저장하는 법 — 올바른 해싱과 솔트

사용자 비밀번호를 어떻게 저장해야 할까? 평문·암호화·생(raw) 해시가 모두 실패하는 이유, 사용자별 솔트가 하는 일, 그리고 진짜 답 — 느린 해시(bcrypt/Argon2/scrypt): 고르고, 튜닝하고, 기존 시스템을 이전하는 법. 공격 절차 없이 방어적·개발자 중심으로.

게시 2026-06-27 업데이트 2026-06-27 4분 읽기

"사용자 비밀번호를 데이터베이스에 정확히 어떻게 저장해야 하지?" — 만드는 사람이라면 한 번은 부딪히는 질문입니다. 답은 명확합니다. 여기 안전하게 저장하는 법을 순서대로, 공격 절차 없이 정리합니다.

평문·암호화·생 해시가 모두 실패하는 이유

언젠가 데이터베이스가 샌다고 가정하세요. 그때 피해는 저장 방식에 따라 천차만별입니다.

  • 평문: 새는 순간 모든 비밀번호가 노출됩니다. 더 나쁜 것은, 비밀번호 재사용이 침해를 사용자의 다른 서비스로 연쇄시킵니다. 최악입니다.
  • 암호화(가역): 키가 그것을 되돌립니다 — 그래서 키가 함께 새면 평문으로 돌아옵니다. 비밀번호는 되읽을 필요가 없으니, 가역성은 아무 이득도 주지 않습니다.
  • 생 해시(MD5/SHA-256): 빠른 해시는 공격자가 추측을 빠르게 시험하게 합니다. 흔한 비밀번호는 레인보우 테이블과 무차별 대입에 무너집니다.

안전으로 가는 "네 단계"

약한 방식에 한 번에 하나씩 보강을 더해 가는 것으로 이해하면 가장 빠릅니다.

1. 평문: 새면 게임 오버
↓ 일방향으로 만든다
2. 생 해시: 레인보우 테이블에 약함
↓ 사용자별 솔트를 더한다
3. 솔트 해시: 테이블은 막았으나 무차별 대입은 여전히 빠름
↓ 의도적으로 느리게 만든다
4. 솔트 + 느린 해시(Argon2id / bcrypt): 이것이 정답
평문 → 생 해시 → 솔트 → 느린 해시. 마지막에 이르러서야 비로소 '안전한 저장'입니다.

핵심 통찰: 솔트와 느린 해시는 서로 다른 일을 합니다. 솔트는 사전 계산(레인보우 테이블)과 대량 재사용 크래킹을 무력화합니다. 느린 해시는 무차별 대입을 비현실적인 속도로 떨어뜨립니다. 안전해지려면 둘 다 필요합니다(→ 해싱이란 무엇인가).

실전: 지금 무엇을 할까

1

Argon2id(또는 bcrypt)를 쓴다

신규 시스템이라면 Argon2id를 1순위로 — 메모리까지 요구할 수 있어 GPU 무차별 대입에 저항합니다. 검증된 구현을 선호한다면 bcrypt도 실전에서 충분합니다. 어느 쪽이든 표준 라이브러리 구현을 그대로 쓰세요.

2

솔트는 자동으로 맡긴다

bcrypt/Argon2는 사용자별 솔트를 생성해 해시 문자열 안에 삽입합니다. 별도의 솔트 컬럼을 둘 필요가 없습니다. MD5(salt + password)를 손수 짜는 것은 전형적인 하지 말아야 할 일입니다.

3

환경에 맞게 비용을 튜닝한다

bcrypt의 비용 인자, 또는 Argon2의 메모리/반복/병렬도를 정상 로그인이 여전히 즉각적으로 느껴지는 한도에서 최대한 높게 설정하세요. 서버가 빨라지면 공격자도 빨라지므로 — 매년 다시 점검해 올리세요.

4

표준 비교 함수로 검증한다

로그인 시 입력을 저장된 해시와 라이브러리의 검증 함수로 대조하세요(대부분 타이밍 안전한 상수 시간 비교를 씁니다). 직접 문자열 동등 비교를 짜지 마세요.

5

약한 해시는 로그인 시 재해싱으로 이전한다

이미 MD5/SHA-256을 저장 중이라면, 사용자가 로그인에 성공하는 순간 새 방식으로 재해싱해 저장하세요. 로그인하지 않은 사용자는 기존 해시를 bcrypt로 감싸(레이어드 해시) 잠정 업그레이드할 수 있습니다.

흔한 실수 vs 올바른 구현

흔한 실수

  • 저장용으로 비밀번호를 암호화(키가 새면 → 끝)
  • MD5(password)SHA-256(password)를 그대로 저장
  • 모든 사용자에게 단일 공유 솔트 사용
  • hash(salt + password)를 손수 짜기

올바른 구현

  • Argon2id / bcrypt로 일방향 해시
  • 사용자별 솔트(라이브러리가 자동으로 추가)
  • 괜찮게 느껴지는 한도에서 높은 비용, 주기적으로 상향
  • 표준 해시/검증 함수를 그대로 사용

본 사이트의 견해: 비밀번호 저장은 지루하고 검증된 답에 올라타는 자리다

비밀번호 저장은 창의력을 발휘할 자리가 아닙니다. Argon2/bcrypt의 세계적으로 검증된 표준 구현에 올라타는 것이 가장 안전하고 운영도 가장 쉽습니다. 우리의 입장: 여기서는 혁신하지 마세요. 그 노력은 대신 비밀번호 의존 자체를 줄이는 데 쓰세요 — 철저한 다단계 인증(MFA), 그리고 결국에는 패스키(패스워드리스)로의 이동입니다. 아무리 강한 해시도 약하고 재사용된 비밀번호를 구할 수는 없습니다.

다음으로 읽기

FAQ

Q저장용으로 비밀번호를 그냥 암호화하면 안 되나요?
A

안 됩니다 — 암호화는 잘못된 도구입니다. 암호화는 키로 되돌릴 수 있어서, 키가 새면 모든 비밀번호가 평문으로 돌아옵니다. 비밀번호는 본인을 포함해 누구도 되읽을 필요가 없으므로, 되돌릴 수 없는 '해시'가 정답입니다. 다만 생 해시만으로는 부족합니다; 사용자별 솔트와 느린 해시(bcrypt/Argon2)를 결합하세요.

Qbcrypt와 Argon2 중 무엇을 써야 하나요?
A

신규 개발이라면 Argon2(특히 Argon2id)가 1순위입니다: 메모리와 연산을 모두 튜닝할 수 있어 GPU 무차별 대입에 잘 저항합니다. 오랜 검증을 거친 구현을 중시한다면 bcrypt도 여전히 충분히 실용적입니다. 어느 쪽이든 핵심은 표준 라이브러리 구현을 그대로 쓰고 직접 만들지 않는 것입니다.

Q이미 MD5/SHA-256으로 비밀번호를 저장했습니다. 어떻게 이전하나요?
A

평문으로 일괄 되돌릴 수 없으므로(그게 핵심입니다) 로그인 시 이전하세요. 사용자가 로그인에 성공하는 순간, 방금 입력한 올바른 비밀번호를 새 방식(예: Argon2id)으로 재해싱해 저장하세요. 로그인하지 않은 사용자는 기존 해시를 bcrypt로 한 번 감싸(레이어드 해시) 잠정 업그레이드할 수 있습니다.