Swift 비밀번호 해싱 반복 횟수 부족

비밀번호 해싱 반복 횟수 부족

설명

PBKDF1은 MD5 또는 SHA-1에만 기반한 레거시 KDF이므로 반복 횟수와 관계없이 새 비밀번호 저장에 사용하면 안 됩니다. PBKDF2도 HMAC-SHA256은 600,000회, HMAC-SHA512는 220,000회보다 낮으면 유출된 해시에 대한 오프라인 대입 공격이 쉬워집니다. 기존 호환성 때문에 HMAC-SHA1을 유지해야 한다면 최소 1,400,000회를 적용하되 새 시스템에서는 선택하지 않습니다.

잠재적 영향

  • 비밀번호 크래킹 비용 감소
  • 유출된 해시의 원문 복구 가능성 증가
  • 인증 정보 재사용 공격 위험 증가

해결 방법

  1. PBKDF1은 호환 목적 외에는 사용하지 말고 기존 비밀번호 해시는 로그인 시 더 안전한 KDF로 마이그레이션합니다.
  2. PBKDF2-HMAC-SHA256은 OWASP 권고 수준인 600,000회 이상으로 설정하고 정책을 주기적으로 갱신합니다.
  3. PBKDF2-HMAC-SHA512를 사용한다면 220,000회 이상으로 설정합니다.
  4. 레거시 PBKDF2-HMAC-SHA1은 1,400,000회 이상을 적용하고 로그인 시 더 현대적인 KDF로 마이그레이션합니다.
  5. 가능하면 Argon2id, bcrypt, scrypt 같은 비밀번호 전용 알고리즘을 사용합니다.
  6. CryptoSwift의 낮은 기본 반복 횟수에 의존하지 말고 반복 횟수를 명시합니다.
  7. 반복 횟수는 설정값으로 관리하되 안전한 최소값 아래로 내려가지 않도록 검증합니다.

예시

변경 전

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에 현재 권고되는 최소 반복 횟수를 적용하고, 서버 성능과 정책 변화에 맞춰 작업 비용을 재평가합니다.

참조