EXERCISE 2 — Spot and fix every cryptographic failure ====================================================== THE DESIGN HAS FOUR A02 FAILURES: FAILURE 1 — Passwords stored as MD5. A02 SUB-CATEGORY: weak password hashing (and arguably "wrong primitive"). WHY IT FAILS: MD5 is a FAST, general-purpose, (here) UNSALTED hash. A breach lets attackers crack billions of guesses/sec on a GPU and use rainbow tables; most passwords fall in minutes. MD5 is also cryptographically broken. FIX: use a SLOW, SALTED password hash — Argon2id (preferred) or bcrypt (cost >= 12). Salt is auto-generated/embedded by these. Force password resets (old MD5s are exposed) and upgrade-on-login. (Auth Ch. 2.) FAILURE 2 — The site served over HTTP. A02 SUB-CATEGORY: cleartext transmission (data in transit unprotected). WHY IT FAILS: everything — passwords, session cookies, personal data — travels in plaintext and can be sniffed or tampered with by anyone on the path (open Wi-Fi, ISP, proxies). SSL-stripping keeps victims on HTTP. FIX: HTTPS EVERYWHERE with a valid certificate + HSTS; redirect HTTP->HTTPS; Secure flag on cookies; cover internal and DB connections too. (HTTPS course.) FAILURE 3 — Encryption key hardcoded in source and committed to git. A02 SUB-CATEGORY: poor key management. WHY IT FAILS: the key is now in the repository (and its entire history, forks, clones, CI logs). Anyone with repo access — or who finds a leaked repo — has the key, which defeats the encryption entirely (the key is the whole secret). Hardcoded/committed secrets are a top real-world breach cause. FIX: NEVER hardcode keys or commit secrets. Load keys from a SECRETS MANAGER / KMS / environment configuration outside source control; rotate the exposed key immediately (treat it as compromised) and purge it from history; keep keys separate from the data they protect; restrict access. Add secret-scanning to CI to catch future commits. FAILURE 4 — Session IDs generated with Math.random(). A02 SUB-CATEGORY: weak randomness (insufficient entropy for a security token). WHY IT FAILS: Math.random() is NOT cryptographically secure — its output is predictable and its internal state can be recovered from observed values. An attacker can predict/forge other users' session IDs and hijack sessions without credentials. (Auth Ch. 4.) FIX: generate session IDs (and any tokens/keys/IVs) with a CSPRNG — crypto.randomBytes(32) (Node) / the platform's secure RNG — with >= 128 bits of entropy. Better: use the framework's session machinery, which already does this. CORRECTED DESIGN (summary): Argon2id/bcrypt password hashing + HTTPS everywhere (HSTS, Secure cookies) + keys from a KMS/secrets manager (never in git, rotated) + CSPRNG-generated session IDs. Each fix maps to an A02 sub-category: weak hashing, cleartext transit, key management, weak randomness. META-LESSON: Three of the four are "use the right, well-built tool correctly" rather than novel crypto — reinforcing "don't roll your own crypto" and lean on vetted libraries/standards (Exercise 3).