설명
PBKDF1은 MD5 또는 SHA-1에만 기반한 레거시 KDF이므로 반복 횟수와 관계없이 새 비밀번호 저장에 사용하면 안 됩니다. PBKDF2도 HMAC-SHA256은 600,000회, HMAC-SHA512는 220,000회보다 낮으면 유출된 해시에 대한 오프라인 대입 공격이 쉬워집니다. 기존 호환성 때문에 HMAC-SHA1을 유지해야 한다면 최소 1,400,000회를 적용하되 새 시스템에서는 선택하지 않습니다.
잠재적 영향
- 비밀번호 크래킹 비용 감소
- 유출된 해시의 원문 복구 가능성 증가
- 인증 정보 재사용 공격 위험 증가
해결 방법
- PBKDF1은 호환 목적 외에는 사용하지 말고 기존 비밀번호 해시는 로그인 시 더 안전한 KDF로 마이그레이션합니다.
- PBKDF2-HMAC-SHA256은 OWASP 권고 수준인 600,000회 이상으로 설정하고 정책을 주기적으로 갱신합니다.
- PBKDF2-HMAC-SHA512를 사용한다면 220,000회 이상으로 설정합니다.
- 레거시 PBKDF2-HMAC-SHA1은 1,400,000회 이상을 적용하고 로그인 시 더 현대적인 KDF로 마이그레이션합니다.
- 가능하면 Argon2id, bcrypt, scrypt 같은 비밀번호 전용 알고리즘을 사용합니다.
- CryptoSwift의 낮은 기본 반복 횟수에 의존하지 말고 반복 횟수를 명시합니다.
- 반복 횟수는 설정값으로 관리하되 안전한 최소값 아래로 내려가지 않도록 검증합니다.
예시
변경 전
swift
import CryptoSwift
func deriveWithTooFewIterations(
password: [UInt8], salt: [UInt8]
) throws -> [UInt8] {
return try PKCS5.PBKDF2(
password: password,
salt: salt,
iterations: 90000,
keyLength: 32,
variant: .sha2(.sha256)
).calculate()
}
func deriveWithObsoletePBKDF1(password: [UInt8]) throws -> [UInt8] {
let salt = (0..<8).map { _ in UInt8.random(in: 0...UInt8.max) }
return try PKCS5.PBKDF1(
password: password,
salt: salt,
iterations: 1000,
keyLength: 16
).calculate()
}
변경 후
swift
import CryptoSwift
func derivePasswordKey(password: [UInt8], salt: [UInt8]) throws -> [UInt8] {
return try PKCS5.PBKDF2(
password: password,
salt: salt,
iterations: 600000,
keyLength: 32,
variant: .sha2(.sha256)
).calculate()
}
설명:
- 변경 전: PBKDF2-HMAC-SHA256의 낮은 반복 횟수는 공격자의 오프라인 대입 시도를 빠르게 만듭니다. PBKDF1은 임의 솔트를 사용해도 MD5/SHA-1 기반의 레거시 KDF이므로 새 비밀번호 해시에 적합하지 않습니다.
- 변경 후: PBKDF2-HMAC-SHA256에 현재 권고되는 최소 반복 횟수를 적용하고, 서버 성능과 정책 변화에 맞춰 작업 비용을 재평가합니다.