パスワードへの汎用ハッシュの使用

パスワードへの汎用ハッシュの使用

説明

SHA-2/3やBLAKE2などの汎用ハッシュをパスワードに直接適用すると、計算コストが低いため、GPUやASICで大量の候補を高速に試せます。漏えいしたハッシュに対して辞書や総当たりを使う攻撃では、弱いパスワードや使い回されたパスワードが特に危険です。データベース、バックアップ、ログからの露出が、オフラインでの解析やアカウントの乗っ取りにつながるおそれがあります。

想定される影響

  • アカウントの乗っ取り: 特定されたパスワードでログインされる可能性があります。
  • 他のサービスへの侵害拡大: 使い回されたパスワードで別のサービスにも侵入されるおそれがあります。
  • 情報漏えいの拡大: 管理者アカウントを奪われると、より広い範囲のシステムやデータにアクセスされる可能性があります。
  • セキュリティ要件の未達: 組織に適用されるパスワード保存要件を満たさない場合、監査で指摘されることがあります。

対処方法

  • Argon2id、scrypt、bcrypt、PBKDF2など、計算コストを調整できるパスワード専用の方式を使ってください。
  • 十分な計算コストを設定してください。
    • Argon2id: 下の例は time_cost=3、メモリ64 MiB、parallelism=2 を使います。運用環境で測定して調整してください。
    • PBKDF2-HMAC-SHA256: 600,000回以上の反復を使い、サーバーの処理能力に合わせてコストを調整してください。
    • bcrypt: ここではワークファクター12以上を推奨します。
    • scrypt: サーバーの処理能力に合わせて、十分なN/r/pを設定してください。
  • ユーザーごとに16バイト以上のランダムなソルトを新しく生成し、ハッシュとともに保存してください。使い回さないでください。
  • ペッパーを使う場合は、データベースとは別の保護された保存先で秘密鍵を管理し、検証済みの組み合わせ方を採用してください。
  • パスワードハッシュのライブラリが提供する検証APIを使ってください。ダイジェストを直接比較する必要がある場合は、hmac.compare_digest などの一定時間で比較する関数を使用してください。
  • 十分なパスワード長、入力長の上限、リクエストの速度制限、アカウントロックなどでオンライン攻撃にも対処してください。
  • argon2-cffiやpasslibなどの専用ライブラリを使用し、独自実装は避けてください。

例

変更前

python
import hashlib

# 不適切な例: 高速な汎用ハッシュ(sha3_256)と固定ソルト
STATIC_SALT = b"NaCl-2024"  # ユーザーごとの個別ソルトではありません

def insecure_hash_password(password: str) -> str:
    data = password.encode("utf-8") + STATIC_SALT
    digest = hashlib.sha3_256(data).hexdigest()
    return digest

# 同じ計算結果を文字列比較して検証(タイミング情報が漏れる可能性)
def insecure_verify_password(password: str, stored_hash: str) -> bool:
    calc = insecure_hash_password(password)
    return calc == stored_hash

変更後

python
from argon2 import PasswordHasher
from argon2.exceptions import VerifyMismatchError

# メモリを必要とするArgon2idでGPU攻撃のコストを増加
ph = PasswordHasher(time_cost=3, memory_cost=64 * 1024, parallelism=2, hash_len=32)

def secure_hash_password(password: str) -> str:
    # 自動生成したランダムなソルトを含む形式でハッシュを生成します。
    return ph.hash(password)

def secure_verify_password(password: str, stored_hash: str) -> bool:
    try:
        return ph.verify(stored_hash, password)
    except VerifyMismatchError:
        return False

解説:

  • 変更前:
    • sha3_256などの高速な汎用ハッシュでは、GPUやASICで低コストに候補を試せます。
    • 固定ソルトは、ユーザー間で同じパスワードのハッシュを同じにし、事前計算された表への防御を弱めます。
    • 通常の文字列比較は一定時間ではなく、タイミング情報が漏れる可能性があります。
  • 変更後:
    • Argon2idはメモリを必要とし、大規模な並列推測のコストを増やします。
    • PasswordHasherは各ハッシュに新しいランダムなソルトを自動で含めます。
    • verifyメソッドで検証し、パラメーターは測定したサーバーの処理能力に合わせて強化できます。

参考資料