bcrypt는 1999년 Niels Provos와 David Mazières가 설계한 비밀번호 전용 해시 함수다. Blowfish 블록 암호를 기반으로 하며, 의도적으로 느리게 설계되어 무차별 대입 공격(Brute Force)과 레인보우 테이블 공격에 강하다. 비밀번호 안전 저장의 업계 표준으로 널리 사용된다.
bcrypt의 핵심 설계 철학
"암호화는 빠를수록 좋지만, 비밀번호 해싱은 느릴수록 좋다."
| SHA-256 (빠름) | bcrypt (느림) |
|---|
| 1초에 수억 회 계산 가능 | 1초에 수십~수백 회만 계산 |
| 빠른 해싱이 오히려 공격에 취약 | 느린 해싱이 공격 비용을 폭발적으로 증가 |
bcrypt 출력 형식
$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewdBdXIG6WFPM0zm
│ │ │ │
│ │ └── 솔트 (22자, Base64) └── 해시값 (31자)
│ └────── cost factor (라운드 수: 2^12 = 4096회 반복)
└────────── 버전 ($2b = bcrypt v2b)
솔트 (Salt)
bcrypt는 자동으로 랜덤 솔트를 생성해 해시에 포함시킨다.
사용자1: bcrypt("password123") → $2b$12$ABC...xyz ← 솔트 ABC
사용자2: bcrypt("password123") → $2b$12$DEF...uvw ← 솔트 DEF
같은 비밀번호라도 결과가 완전히 다름
→ 레인보우 테이블 공격 무력화
Cost Factor (작업 인수)
cost = 10 → 2^10 = 1,024번 반복
cost = 12 → 2^12 = 4,096번 반복 (권장)
cost = 14 → 2^14 = 16,384번 반복 (고보안)
cost가 1 증가할 때마다 계산 시간 2배 증가
→ 하드웨어가 빨라져도 cost를 높여 동일한 보안 유지
코드 예시
python
# Python (bcrypt 라이브러리)
import bcrypt
# 비밀번호 해싱
password = b"my_password"
hashed = bcrypt.hashpw(password, bcrypt.gensalt(rounds=12))
# b'$2b$12$...'
# 검증
bcrypt.checkpw(password, hashed) # True
bcrypt.checkpw(b"wrong", hashed) # False
javascript
// Node.js (bcryptjs)
const bcrypt = require('bcryptjs');
// 해싱
const hash = await bcrypt.hash('my_password', 12); // cost=12
// 검증
const isValid = await bcrypt.compare('my_password', hash); // true
java
// Spring Security (Java)
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(12);
String hash = encoder.encode("my_password");
encoder.matches("my_password", hash); // true
bcrypt vs 다른 비밀번호 해시 함수
| 함수 | 설계 연도 | 특징 | 현재 권장도 |
|---|
| bcrypt | 1999 | Blowfish 기반, 널리 지원 | ✓ 권장 |
| scrypt | 2009 | 메모리 집약적, GPU 공격 저항 | ✓ 권장 |
| Argon2 | 2015 | PHC 우승, 메모리+시간+병렬 조절 | ✓ 최신 권장 |
| PBKDF2 | 2000 | NIST 표준, GPU 취약 | △ 레거시 |
| MD5/SHA-256 | — | 빠름 → 비밀번호에 절대 사용 금지 | ✗ 사용 금지 |
일반적인 보안 실수
python
# ✗ 잘못된 방법 — SHA-256은 비밀번호에 부적합
import hashlib
hashed = hashlib.sha256(password.encode()).hexdigest()
# ✗ 잘못된 방법 — 솔트 없이 저장
hashed = hashlib.md5(password).hexdigest()
# ✓ 올바른 방법 — bcrypt 사용
import bcrypt
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt(12))
관련 개념
- •SHA-256 — 빠른 해시 함수 (비밀번호에 부적합)
- •해시 (Hash) — 해시 함수 일반 개념
- •공개키 암호화 — 암호학 기반 개념
- •SSL/TLS — 전송 계층 보안
참고문헌
- •Provos, N. & Mazières, D. (1999). A Future-Adaptable Password Scheme (USENIX)
- •OWASP Password Storage Cheat Sheet
- •NIST SP 800-63B — Digital Identity Guidelines