復元可能な保存形式とは
復元可能性でランク付けされた4つの保存形式:
| 形式 | 復元可能? | 例 | 復元方法 |
|---|---|---|---|
| 平文 | はい | password: hunter2 | ファイルを読み込む |
| Base64 | はい | cGFzc3dvcmQ= | base64 --decode |
| 可逆暗号 (AES) | はい | ENC[AES256:...] | キーで復号 |
| 一方向ハッシュ (bcrypt) | いいえ | $2b$12$... | 逆変換不可; ブルートフォースが必要 |
平文、Base64、可逆暗号: すべて復元可能。1 回の認証情報データベースダンプで、攻撃者は全ユーザーのパスワードを平文で同時に取得できる。1 回の侵害で全暴露。
Mailman 2.x の例
Mailman 2.x(GNU メーリングリストマネージャー): 購読者のパスワードを平文で保存。毎月のパスワードリマインダーメール: すべての購読者に平文でパスワードを送信。2 つの別個の欠陥、いずれも MOAD-0006:
1. 保存: リストデータベースに平文で保存。サーバー侵害により全購読者のパスワードが暴露される。
2. 配信: 毎月のメールで平文のパスワードを SMTP で購読者のメールサーバーへ送信。メールは複数の SMTP ホップを平文で通過する。
Mailmanチームは両方の動作を設計しました。リカバリーは意図された機能でした。加入者は忘れたパスワードを取り戻すことができました。Glass Safeという名称はここに由来します。金庫は資格情報を可視の形で保持します。金庫に到達した人は誰でも、すべての内容を同時に読むことができます。
すでに盗まれた原則
リカバリー可能な形式で保存された資格情報は、すでに盗まれた資格情報です。攻撃者はまだ到着していません。侵害はまだ発生していません。しかし、アーキテクチャは保証します。侵害が発生したとき、すべての資格情報が同時に失われます。侵害は孤立して発生するものではありません。リカバリー可能なストレージ内のすべての資格情報が、同じ操作で攻撃者に移されます。
MOAD-0006 vs MOAD-0004
MOAD-0004(Logged Secret):資格情報が誤ってログに書き込まれた。ログへの書き込みは意図したものではなく、デバッグのためにヘッダーログを有効にした副作用でした。
MOAD-0006(Glass Safe):資格情報が意図的にリカバリー可能な形式で保存された。リカバリーが意図でした。パスワードリマインダー機能にはパスワードの保存が必要でした。パスワード表示機能にも保存が必要でした。リカバリーへのアーキテクチャ上のコミットメントが欠陥を生みました。
一行の違い:MOAD-0004は資格情報を誤ってログに残します。MOAD-0006は資格情報を意図的にリカバリー可能な形式で保存します。修正は異なるレイヤーで機能します。
構造的 vs 偶発的
MOAD-0006 と MOAD-0004 のアーキテクチャ上の違いが修正戦略を決定する。偶発的なログ書き込みの場合:シリアライズ層を修正する。復元を前提とした設計の場合:復元を必要とする機能を再設計する。
bcrypt が機能する理由
一方向ハッシュ関数はパスワードを受け取り、固定長のダイジェストを生成する。ダイジェストから元の<|eos|>
資格情報保存に必要な3つの特性:
1. 一方向性(原像耐性). hash(password) が与えられたとき、総当たり攻撃より速く password を復元できるアルゴリズムは存在しない。bcrypt、scrypt、argon2 はすべてこの特性を満たす。
2. ソルト. パスワードをハッシュする前にランダムな値を先頭に付加する。同じパスワードでも異なるソルトを使えば異なるハッシュ値になる。目的: レインボーテーブル(事前計算されたハッシュ辞書)の無効化。ソルトなしの場合: 攻撃者は hash('password123') を1回計算するだけで、100万人の全ユーザーを同時に検証できる。ソルトありの場合: 同じパスワードでも各ユーザーは固有のハッシュ値を持つ。
3. 意図的な低速化. bcrypt は work factor を受け付ける。work factor を大きくすると反復回数が増え、ハッシュ1回あたりの計算時間が長くなる。ログイン時: 1回のハッシュに300ms。許容範囲内。総当たり攻撃時: 1回の試行に300ms。10億回の試行では1パスワードあたり9.5年。攻撃者にとって非現実的。この低速さは欠陥ではなく、意図された機能である。
import bcrypt
# 保存: ソルト付きの一方向ハッシュ
def store_password(plaintext: str) -> bytes:
return bcrypt.hashpw(plaintext.encode(), bcrypt.gensalt(rounds=12))
# 検証: 候補パスワードをハッシュ化してダイジェストを比較
def verify_password(plaintext: str, stored_hash: bytes) -> bool:
return bcrypt.checkpw(plaintext.encode(), stored_hash)
# 絶対に保存しない: 平文パスワード
# 復元不可能:ハッシュから平文は決して取り戻せない
# パスワードリセットではなく、パスワードリマインダー
トレードオフ
一方向ハッシュにより、パスワードの復元は不可能になります。パスワードを忘れたユーザーは、それを取り戻すことはできません。パスワードリマインダーメールは存在し得ません。ユーザー体験は変わります:「パスワードをお忘れですか? 再設定してください。」これは機能の低下ではなく、セキュリティ境界です。パスワードを復元できないシステムは、パスワードを漏洩させることもできません。
bcryptハッシュを公開するデータベース侵害:すべてのハッシュは可視だが、パスワードは一切見えない。攻撃者は各ハッシュを個別にブルートフォースする必要があり、1回の試行に300msを要し、ユーザーごとのソルトにより事前計算テーブルが無効化される。平文パスワードを公開する侵害:即時の全面露出。
強力な暗号化だけでは不十分
セキュリティ監査で、認証情報保存システムが特定されました。パスワードはサーバー側鍵を使ったAES-256-CBC暗号化で保存されています。監査報告書はこれをGlass Safe欠陥として指摘しています。
エンジニアリングチームの回答:「AES-256は現在利用可能な最も強力な対称暗号です。鍵はハードウェアセキュリティモジュールに保存されています。攻撃者はこれらのパスワードを復号できません。」