パスワードハッシュの基礎と実践ガイド:ソルト・ペッパーからArgon2まで、最新のベストプラクティスと実装ポイント
パスワードハッシュとは何か — 概要
パスワードハッシュとは、平文のパスワードを一方向の関数(ハッシュ関数や鍵導出関数)で処理して得られる固定長のデータで、元のパスワードを直接復元できない形に変換したものを指します。認証システムでは、ユーザが入力したパスワードを同じハッシュ処理にかけ、保存してあるハッシュと一致すれば認証を許可します。パスワードを直接保存する代わりにハッシュを保存することで、データベース流出時のリスクを大幅に下げられます。
なぜ「暗号化」ではなく「ハッシュ」なのか
暗号化は復号キーを用いて元に戻せる双方向の処理です。パスワードを暗号化して保存すると、復号鍵が漏れた場合に全ユーザのパスワードが一度に露見します。一方、ハッシュは一方向性(不可逆)で設計されており、ハッシュ値から元のパスワードを直接導き出せないため、漏洩時の被害を緩和できます(ただし対策が不要になるわけではありません)。
基本要素:ソルト(salt)とペッパー(pepper)
安全なパスワードハッシュには少なくとも「ソルト」が必要です。ソルトは各ユーザごとにランダムに生成される値で、ハッシュ前にパスワードと結合して用います。ソルトを使うことで同じパスワードが複数アカウントで使われていてもハッシュ値は異なり、レインボーテーブル攻撃を無効化できます。推奨されるソルト長は少なくとも16バイト(128ビット)以上で、暗号学的に安全な乱数で生成します。
ペッパーはサーバ側で共通に保持する追加シークレット(短い鍵のようなもの)で、データベースと分離して保管することで、データベースだけが漏えいした場合の防御層を追加します。ただし、運用が複雑になるため必須ではなく、適切に管理できる場合に限定して導入します。
単純なハッシュ(MD5/SHA1など)はなぜ不十分か
MD5やSHA-1、SHA-256などの汎用ハッシュ関数は高速に計算できるため、総当たり攻撃(ブルートフォース)やGPU/ASICを使った高速クラッキングに弱いです。これらはパスワード検証用途向けではなく、衝突耐性など別の目的のために設計されています。パスワード保存には「遅く」「パラメータ調整が可能な」「メモリ消費を増やせる」関数が推奨されます。
推奨されるアルゴリズム:bcrypt, scrypt, Argon2, PBKDF2
- bcrypt:古くから使われている適応的ハッシュ。計算コスト(コスト因子)を上げられるが入力長に制限(約72バイト)があり、メモリハードではない。互換性が高く幅広くサポートされている。
- PBKDF2:HMACベースの鍵導出関数。反復回数を増やすことで計算負荷を高められるが、メモリハード性はなく専用ハードウェアで高速化されやすい。標準規格として広く用いられる場面がある。
- scrypt:Colin Percival により設計されたメモリ集約型関数で、GPU/ASIC による並列化耐性を改善することを目的としている。
- Argon2:Password Hashing Competition(2013–2015)で勝者となった設計で、Argon2i/Argon2d/Argon2id のバリエーションがある。Argon2id(ハイブリッド)は一般的な推奨で、メモリ使用量、反復回数、並列度を調整できる。現在のベストプラクティスとして広く推奨されている。
パラメータ設定の考え方
最適なパラメータは「現在の(および想定する将来の)攻撃用ハードウェアに対して十分にコストをかけ、かつ認証処理で許容できる遅延に収まること」が目標です。実務上の指針としては、ハッシュ計算に対してサーバ側で約100ms前後(0.01〜0.2秒)の処理時間を目安にチューニングすることがよく示されます。Argon2ならメモリ量(MB〜GB)、反復回数、並列度を調整します。定期的にパラメータを見直し、必要なら段階的にリハッシュ(ユーザが次回ログインした際に新パラメータで再ハッシュ)を行います。
保存フォーマットと検証の実装上の注意
ハッシュを保存する際は、ハッシュ値だけでなく使用したアルゴリズム名、パラメータ(コスト因子やメモリ量、反復回数)、ソルト、場合によってはバージョン番号を一緒に保存します。PHC(Password Hashing Competition)フォーマットのように、"$argon2id$v=19$m=65536,t=3,p=4$
攻撃手法とその防御策
- レインボーテーブル:ソルトで無効化。必ずランダムなソルトを使う。
- 総当たり(ブルートフォース)/辞書攻撃:遅い(高コスト)ハッシュ関数と長いソルト、適切なパラメータで難度を高める。加えてアカウントロックやレート制限でオンライン攻撃を抑える。
- GPU/ASIC を使った大規模クラッキング:メモリハード(scrypt, Argon2)や十分に高いコスト設定で対抗。
- データベース流出時の追跡:ペッパーを分離して運用すれば追加の保護層になるが、運用負荷とリスクを考慮する。
運用上のベストプラクティス
- 現行の推奨アルゴリズム(例:Argon2id)を使用する。互換性やライブラリサポートを確認。
- 各ユーザに対してユニークなランダムソルト(>=16バイト)を生成して保存する。
- ハッシュ関数のパラメータは明示的に保存し、将来のパラメータ変更(リハッシュ)に備える。
- パスワード政策(最小長、安全な文字種の推奨)に加え、多要素認証(2FA)を導入する。
- ログイン試行のレート制限、CAPTCHA、アカウントロックを組み合わせてオンライン攻撃を緩和する。
- ソースコードや環境でハードコーディングされた「共通ペッパー」や秘密鍵を置かない。シークレットは安全なシークレットストア(KMS、Vaultなど)で管理する。
- 古いアルゴリズム(MD5, SHA1 単体など)で保存されている既存ユーザは、段階的により安全な方式へ移行する(ログイン時に再ハッシュ、パスワードリセットの促進など)。
移行戦略(レガシーからモダンへ)
既に古い方法でハッシュを保存している場合、一般的な移行方法は「オンデマンドリハッシュ」です。ユーザがログインした際に入力されたパスワードを新しいアルゴリズムでハッシュし直して保存する方法です。全ユーザに対して強制的にパスワード変更を要求するやり方もあるが、ユーザ体験を損ねるため、段階的移行が現実的です。
まとめ
パスワードハッシュはユーザ認証の基礎であり、適切なアルゴリズム選定、ランダムソルトの付与、パラメータのチューニングと継続的な見直しが不可欠です。Argon2(特にArgon2id)は現時点での推奨候補であり、メモリハード性と可変パラメータにより将来の攻撃に対しても耐性を備えています。加えて、多要素認証やレート制限、シークレット管理の徹底といった複数の防御層を組み合わせることが重要です。
参考文献
- OWASP Password Storage Cheat Sheet
- NIST Special Publication 800-63B — Digital Identity Guidelines
- RFC 2898 — PKCS #5: Password-Based Cryptography Specification (PBKDF2)
- scrypt(Colin Percival)
- Password Hashing Competition (PHC)
- PHC string format specification
- bcrypt(概要)
- PHP: password_hash — マニュアル


