Swiftでのパスワードハッシュの反復回数不足

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に現在推奨される最小反復回数を適用しています。サーバー性能やポリシーの変化に応じて計算コストを見直してください。

参考資料