どのようなストレージ形式が回復可能か
四つのストレージ形式、回復可能性でランク付け:
| 形式 | 回復可能? | 例 | 回復方法 |
|--------|-------------|---------|-----------------|
| プレーンテキスト | はい | password: hunter2 | ファイルを読み取る |
| Base64 | はい | cGFzc3dvcmQ= | base64 --decode |
| 逆戻りシフル (AES) | はい | ENC[AES256:...] | デコードキーで暗号化解除する |
| 一方向ハッシュ (bcrypt) | いいえ | $2b$12$... | 逆戻りできないので、強制的に暴力攻撃が必要 |
プレーンテキスト、Base64、逆戻りシフル: すべて回復可能。1つのクレデンシャルデータベースダンプで、攻撃者がすべてのパスワードをクリアテキストで取得する。1つの侵害; 総露出。
Mailman 2.xの例
Mailman 2.x (GNU メールリストマネージャー): サブスクリバーパスワードをプレーンテキストで保存。月次パスワードリマインダーEメール: サブスクリバーにプレーンテキストのパスワードを送信。二つの別々の欠陥、両方ともMOAD-0006:
1. ストレージ: リストデータベースにプレーンテキスト。サーバー侵害ですべてのサブスクリバーのパスワードが露出される。
2. ブロードキャスト: 月次EメールでプレーンテキストのパスワードをSMTPでサブスクリバーのEメールサーバーに送信。Eメールは複数のSMTPホップを通じてクリアテキストで移動する。
Mailmanチームが両方の機能を設計した。回復が機能: サブスクリバーが忘れられたパスワードを取得できる。ガラスセーフの名前はこれから来て: セーフはクレデンシャルを可視形で保持している。セーフに到達したあらゆる人が同時にすべての内容を読み取ることができる。
既に盗まれた原則
クレデンシャルが回復可能な形式で保存されている場合、それは既に盗まれたクレデンシャルである。攻撃者がまだ到着していない。侵害がまだ起こされていない。しかし、アーキテクチャーはすべてのクレデンシャルが同時に転送されることを保証する。どの侵害も孤立して起こされない; 回復可能なストレージにあるすべてのクレデンシャルは同じ操作で攻撃者に転送される。
MOAD-0006 vs MOAD-0004
MOAD-0004 (Logged Secret): 障害の原因となったログへのクレデンシャルが書き込まれた。ログの書き込みが意図されていなかった。ヘッダーをデバッグするために有効にしたもので、ログの書き込みが副作用だった。
MOAD-0006 (Glass Safe): クレデンシャルが回復可能な形式でデザインで保存されている。回復が意図されていた。パスワードリマインダー機能で保存する必要があった。表示パスワード機能で保存する必要があった。回復へのアーキテクチャーのコミットメントは欠陥を作成した。
One-line distinction: MOAD-0004 は偶然ログに資格情報を記録する; MOAD-0006 は意図的に回復可能な形式で資格情報を保存する。修正は異なるレイヤーで作用します。
構造的 vs偶然の
MOAD-0006 と MOAD-0004 の間のアーキテクチャ的違いが修正戦略を決定します。偶然のログ書き込み: シリアル化レイヤーを修正します。設計上の回復ストレージ: 回復が必要な機能をリデザインします。
bcrypt の効果
一方向ハッシュ関数はパスワードを受け取り、定長のダイジェストを生成します。ダイジェストを与えられた場合、元のパスワードが回復されないことに注意してください。'困難に回復される': 逆算が不可能です。関数は一方向のみで実行されます。
資格情報の保存に必要な3つのプロパティがあります:
1. 一方向 (プレイメージ抵抗)。 hash(password) を与えられた場合、元の password を回復するアルゴリズムは存在しませんが、暴力的なフォースで回復することができます。bcrypt、scrypt、および argon2 はすべてこのプロパティを満たしています。
2. 塩。 パスワードにランダムな値を前置きしてハッシュします。同じパスワードでも、塩が異なれば異なるハッシュになります。目的:レインボータブル(事前計算されたハッシュ辞書)を破ります。塩がない場合:攻撃者は hash('password123') を一度計算して、すべての1,000万のユーザーと同時にチェックします。塩がある場合:同じパスワードでも各ユーザーにはユニークなハッシュが存在します。
3. Slow by design. bcrypt accepts a work factor. Higher work factor: more iterations, more compute time per hash attempt. Login: 300ms to hash once. Acceptable. Brute-force: 300ms per attempt. At 1 billion attempts: 9.5 years per password. Unacceptable for an attacker. The slowness: a feature, not a defect.
import bcrypt
# Store: one-way hash with salt
def store_password(plaintext: str) -> bytes:
return bcrypt.hashpw(plaintext.encode(), bcrypt.gensalt(rounds=12))
# Verify: hash the candidate & compare digests
def verify_password(plaintext: str, stored_hash: bytes) -> bool:
return bcrypt.checkpw(plaintext.encode(), stored_hash)
# NEVER stored: the plaintext password
# NEVER recovered: the plaintext from the hash
# Password reset, not password reminder
The Tradeoff
One-way hashing makes password recovery impossible. A user who forgets their password cannot receive it back. A password reminder email cannot exist. The user experience changes: 'forgot your password? reset it.' Not a degradation: a security boundary. The system that cannot recover a password cannot leak a password.
A database breach that exposes bcrypt hashes: all hashes visible, no passwords visible. An attacker must brute-force each hash individually, at 300ms per attempt, with per-user salt defeating precomputed tables. A breach that exposes plaintext passwords: total immediate exposure.
Strong Encryption Is Not Enough
セキュリティーアuditがクレデンシャルストレージシステムを特定します。パスワードは、サーバーサイドキーで使用されるAES-256-CBC暗号化とともに格納されています。アuditレポートで、Glass Safe欠点として警告されています。
エンジニアチームは返答します、「AES-256は最強のシミュレーション暗号です。キーはハードウェアセキュリティモジュールに存在します。攻撃者はこれらのパスワードを解読することはできません。」