설명
비밀번호를 MD5, SHA-1 같은 빠른 범용 해시로 저장하면, 공격자가 해시값을 탈취했을 때 GPU/클라우드로 매우 빠르게 대입 공격(brute force)과 사전 공격을 수행할 수 있습니다. 솔트가 없거나 코스트(반복/메모리)가 낮으면 더욱 취약합니다. 결과적으로 해시에서 원문 비밀번호를 복구해 계정 탈취로 이어질 수 있습니다. 주된 악용 방식은 데이터베이스 유출·백업 노출 등으로 해시를 획득한 뒤, 도구(Hashcat 등)로 대량 크래킹을 시도하는 것입니다.
잠재적 영향
- 계정 탈취: 해시가 빠르게 크랙되어 사용자 계정에 무단 로그인 가능
- 권한 상승: 탈취한 관리자/운영 계정으로 시스템 통제 권한 확보
- 데이터 유출: 계정 접근을 통해 민감 정보 조회 및 다운로드
- 크리덴셜 스터핑 확대: 복구한 비밀번호를 다른 서비스에 재사용 시도
- 규정 준수/평판 손상: 보안 기준 미준수로 법적·신뢰도 리스크 발생
해결 방법
- 새 비밀번호 저장에는 Argon2id를 우선 검토하고, 사용할 수 없다면 scrypt를 고려하세요. bcrypt는 기존 시스템 호환이 필요한 경우, PBKDF2는 관련 요구사항이 있는 경우 사용하세요.
- 현재 권고를 기준으로 비용을 설정하고 운영 환경에서 측정하세요. 예: Argon2id
m≥19 MiB, t≥2, p=1, scryptN≥2^17, r=8, p=1, bcryptcost≥10, PBKDF2-HMAC-SHA256600,000회 이상입니다. - 비밀번호마다 고유한 난수 솔트를 사용하세요. 라이브러리가 솔트를 생성·저장한다면 그 기능을 사용하세요.
- 페퍼를 추가한다면 비밀번호 DB와 분리된 비밀 저장소나 HSM에서 관리하세요. 페퍼가 솔트나 충분한 연산 비용을 대신하지는 않습니다.
- 로그인에 성공한 뒤 약한 기존 해시를 현재 알고리즘과 비용으로 다시 저장하세요.
예시
기존 bcrypt 사용을 유지하는 비교입니다. bcrypt는 UTF-8 입력의 처음 72바이트만 사용하므로, 등록과 검증에 일관된 입력 정책을 적용하고 긴 비밀번호가 조용히 잘리지 않도록 하세요. 비용 12는 예시이며 배포 환경에서 부하를 측정해야 합니다.
변경 전
javascript
// 취약: 빠른 범용 해시(MD5/SHA-1)로 비밀번호 저장
const crypto = require("crypto");
async function saveUserWeak(username, plainPassword, db) {
const digest = crypto
.createHash("md5")
.update(plainPassword, "utf8")
.digest("hex");
await db.users.insert({ username, passwordHash: digest });
}
변경 후
javascript
// 안전: bcrypt로 고유 솔트 + 충분한 cost 사용
const bcrypt = require("bcrypt");
async function saveUserSecure(username, plainPassword, db) {
const cost = 12; // 코스트 팩터 예시 (배포 환경에서 측정)
const hash = await bcrypt.hash(plainPassword, cost); // 내부적으로 랜덤 솔트 포함
await db.users.insert({ username, passwordHash: hash });
}
async function verifyLogin(username, inputPassword, db) {
const user = await db.users.findOne({ username });
if (!user) return false;
return bcrypt.compare(inputPassword, user.passwordHash);
}
설명:
- 변경 전: MD5는 매우 빠른 해시로 설계되어 GPU/ASIC에서 초당 막대한 시도로 크랙이 가능합니다. 솔트/코스트가 없어 대입·사전 공격과 레인보우 테이블 공격에 특히 취약합니다.
- 변경 후: bcrypt는 각 비밀번호에 고유한 솔트를 포함하고, 코스트 팩터로 연산량을 크게 늘려 대입 공격 비용을 높입니다. 예시는 코스트 12로 해시를 저장하고
bcrypt.compare로 검증합니다. 강한 비밀번호 정책과 로그인 시도 제한도 함께 적용하세요.