🎓 レベル:基礎 | 重要度:A(必須) 📎 前提:対称鍵暗号とハッシュ
要点(BLUF)
- 認証は「知識(パスワード)・所持(端末/鍵)・生体(指紋)」のどれかで本人を確かめること。
- パスワードは平文でも SHA-256 でもなく、ソルト付きの**低速 KDF(Argon2id / bcrypt / PBKDF2)**で保存する。
- 単一要素は漏れる前提。**多要素認証(MFA)**で「知識+所持」など別種を重ね、1つ漏れても突破させない。
概念:3つの認証要素
認証は「主張する本人であること」をどう確かめるかで、材料(要素)は3種類です。
- 知識(something you know):パスワード、PIN。漏れやすく使い回されやすい。
- 所持(something you have):スマホの認証アプリ、ハードウェアキー、ワンタイムトークン。
- 生体(something you are):指紋、顔。変更できないのが弱点。
1種類だけ(多くはパスワード)に頼るのが弱い理由は、漏えい・使い回し・推測のどれかで突破されうるからです。だから異なる種類を組み合わせるのが基本戦略になります。
仕組み:パスワードの安全な保存
守る側の最重要ポイントは「漏れても困らない形で保存する」ことです。DB はいつか漏れる前提で設計します。
- 平文保存は論外:漏れたら即全アカウント突破。
- 高速ハッシュ(SHA-256 など)単体も不可:速すぎて大量の総当たりに耐えない(対称鍵暗号とハッシュ)。
- 正解は「ソルト付きの低速 KDF」:
- ソルト:利用者ごとに異なるランダム値を混ぜ、同じパスワードでも保存値を変える。事前計算表(レインボーテーブル)を無力化。
- 低速化(ストレッチング):わざと計算を重く反復し、1回の検証コストを上げて総当たりを非現実的にする。
推奨アルゴリズムは Argon2id(メモリ困難・第一候補)、次いで bcrypt、環境制約があれば PBKDF2 や scrypt(いずれも要最新確認のパラメータで)。OWASP の目安では bcrypt はコスト 10 以上、Argon2id はメモリ・反復・並列度を環境に合わせて設定します。
仕組み:多要素認証(MFA)
MFA は異なる種類の要素を2つ以上要求します(知識+所持など)。同種を2つ(パスワード2個)では意味がありません。
- TOTP:認証アプリが時刻ベースで使い捨てコードを生成。
- ハードウェアキー(FIDO2/WebAuthn):公開鍵暗号(公開鍵暗号)ベースで、フィッシングに強い現行ベストの一つ。
- SMS コード:MFA 無しよりは良いが、回線乗っ取り等のリスクがあり推奨度は下がる(要最新確認)。
flowchart TD
L["ログイン要求"] --> F1{"第1要素:パスワード照合(KDFで検証)"}
F1 -->|"一致"| F2{"第2要素:所持の確認(認証アプリ・鍵)"}
F1 -->|"不一致"| DENY["拒否(安全側に倒す)"]
F2 -->|"確認OK"| OK["認証成功(セッション発行へ)"]
F2 -->|"確認NG"| DENY
防御側の使い方/設定
- 保存は Argon2id(または bcrypt)+自動ソルト:ライブラリ標準のソルト管理を使い、自前で混ぜない。
- 検証は専用の照合関数で:平文比較や生ハッシュ比較ではなく、ライブラリの
checkpw等を使い、タイミング差も抑える。 - MFA を既定に:特に管理者・高権限アカウントは必須化。フィッシング耐性の高い方式を優先。
- アカウントロック/レート制限:連続失敗にバックオフをかけ、総当たりを鈍らせる(可用性とのバランスに注意)。
- パスワードポリシーは長さ重視:複雑さの強制より十分な長さと漏えいパスワードの拒否(要最新確認)。
なぜ安全か:低速化が攻撃コストを跳ね上げる
低速 KDF が効くのは、正規の1回のログインでは無視できる遅延でも、総当たりでは天文学的なコストになるからです。ソルトは「1人破る労力で全員は破れない」ようにし、事前計算表を使えなくします。MFA は「片方の要素が漏れても、もう一方が無ければ入れない」状態を作り、パスワード漏えい単体を無効化します。
security-study/labs/password_hashing_demo.py の実行結果(抜粋):
=== 1. bcrypt:同じパスワードでも保存値が毎回変わる ===
hash1 = $2b$12$xlvO3YgYm7q0iIybaWWE/.vF/C0dcqMpb1sVoGG8arD/t7Fmu5pX.
hash2 = $2b$12$jLWNHp67XN1aZlRrC0spLO7UBd1FEQV0ZTzpUzH7WFOnd6obUWbxG
2つは別物 -> True
=== 2. 検証は専用の照合関数で ===
正しいパスワード -> True
誤ったパスワード -> False
同じパスワードでも保存値が毎回変わる(ソルト内蔵)こと、検証は照合関数で行うことが確認できます。
仕組みの直観
パスワード保存は金庫のダイヤル錠に似ています。平文保存は「金庫の番号を扉に貼る」、高速ハッシュは「すぐ回せる軽いダイヤル」。低速 KDF は「1回転に数秒かかる重いダイヤル+人ごとに違う初期位置(ソルト)」。正規利用者は1回だけ回すので平気ですが、総当たりは回し切れません。MFA は「ダイヤルに加えて物理鍵も要る」二重ロックです。
⚠️ よくある誤解・設定ミス
- パスワードを平文/SHA-256 で保存:最頻出の重大ミス。低速 KDF へ。
- ソルトを使わない/全員同じソルト:事前計算表に弱くなる。利用者ごとにランダム。
- MFA に同種要素を重ねる:パスワード+秘密の質問は両方「知識」。別種を組み合わせる。
- エラーメッセージで存在を漏らす:「ユーザーが存在しません/パスワードが違います」を分けると、アカウント列挙の手がかりになる。一般化する。
- 無制限のログイン試行:レート制限・バックオフが無いと総当たりを許す。
対応 lab
security-study/labs/password_hashing_demo.py— bcrypt/PBKDF2 による安全な保存と照合
関連
- 保存に使うハッシュの原理 → 対称鍵暗号とハッシュ
- 認証後の状態管理 → セッションとトークン(JWT)
- 認可(何をしてよいか) → アクセス制御モデルとゼロトラスト