un

guest
1 / ?
back to lessons

What Makes a Storage Format Recoverable

Four storage formats, ranked by recoverability:

| Format | Recoverable? | Example | Recovery Method |

|--------|-------------|---------|-----------------|

| Plaintext | Yes | password: hunter2 | Read the file |

| Base64 | Yes | cGFzc3dvcmQ= | base64 --decode |

| Reversible cipher (AES) | Yes | ENC[AES256:...] | Decrypt with key |

| One-way hash (bcrypt) | No | $2b$12$... | Cannot reverse; must brute-force |

Plaintext, base64, & reversible cipher: all recoverable. A single credential database dump gives an attacker all passwords in cleartext, for all users, simultaneously. One breach; total exposure.

The Mailman 2.x Example

Mailman 2.x (GNU mailing list manager): stored subscriber passwords in plaintext. Monthly password reminder email: sent all subscribers their password in cleartext. Two separate defects, both MOAD-0006:

1. Storage: plaintext in the list database. A server compromise exposes all subscriber passwords.

2. Broadcast: monthly email sends the plaintext password over SMTP to the subscriber's email server. Email travels in cleartext across multiple SMTP hops.

The Mailman team designed both behaviors. Recovery was the feature: subscribers could retrieve forgotten passwords. The Glass Safe name comes from this: the safe holds credentials in visible form. Anyone who reaches the safe can read all contents simultaneously.

The Already-Stolen Principle

A credential stored in recoverable form is a credential already stolen. The attacker has not arrived yet. The breach has not happened yet. But the architecture guarantees: when a breach occurs, all credentials fall simultaneously. No breach occurs in isolation; every credential in recoverable storage transfers to the attacker in the same operation.

MOAD-0006 vs MOAD-0004

MOAD-0004 (Logged Secret): credentials written to logs accidentally. The log write was not the intent; it was a side effect of enabling header logging for debugging.

MOAD-0006 (Glass Safe): credentials stored in recoverable form by design. Recovery was the intent. The password reminder feature required storing the password. The display-password feature required storing it. The architectural commitment to recovery created the defect.

One-line distinction: MOAD-0004 puts credentials in logs by accident; MOAD-0006 stores credentials in recoverable form on purpose. The fixes operate at different layers.

Structural vs Accidental

The architectural distinction between MOAD-0006 & MOAD-0004 determines the fix strategy. An accidental log write: fix the serialization layer. A designed-for-recovery storage: redesign the feature that required recovery.

Why is a Glass Safe structurally different from MOAD-0004? One line describes each defect. What does it mean for the fix?

Why bcrypt Works

A one-way hash function accepts a password & produces a fixed-length digest. Given the digest, the original password cannot be recovered. Not 'difficult to recover': impossible to reverse. The function runs in one direction only.

Three properties required for credential storage:

1. One-way (preimage resistance). Given hash(password), no algorithm recovers password faster than brute-force. bcrypt, scrypt, & argon2 all satisfy this property.

2. Salt. A random value prepended to the password before hashing. Same password, different salt, different hash. Purpose: defeats rainbow tables (precomputed hash dictionaries). Without salt: an attacker computes hash('password123') once & checks all 1 million users simultaneously. With salt: each user has a unique hash even for the same password.

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

A security audit identifies a credential storage system. Passwords are stored using AES-256-CBC encryption with a server-side key. The audit report flags it as a Glass Safe defect.

The engineering team responds: 'AES-256 is the strongest symmetric cipher available. The key lives in a hardware security module. No attacker can decrypt these passwords.'

Why does this remain MOAD-0006 even with strong encryption? What is the correct fix?