No. 04 — Lab 04
Password
storage
If a site's database leaks, the way passwords were stored decides how much damage follows. This lab shows the common mistake, the fix, and what the fix costs, using a short Python script with no dependencies. Every block below is real output.
01 — The mistake
Same input, same hash
Two users, alice and bob, pick the same weak value. The site stores a fast, unsalted hash of it, the way many old systems did.
python password-hash-demo.py plain
alice MD5 f065d609e55983bc6087c073c91c9bc7 alice SHA-256 323725e8eff4df0a4974d6ea8c73017aa6467d94e09382745b7a988cec0fba0a bob MD5 f065d609e55983bc6087c073c91c9bc7 bob SHA-256 323725e8eff4df0a4974d6ea8c73017aa6467d94e09382745b7a988cec0fba0a Same input, same hash. Anyone reading the database can see alice and bob chose the same value, and one precomputed table of hashes works against every account.
02 — The fix
A random salt and a slow hash
Each user gets their own random salt, and the hash is scrypt, which is deliberately slow and memory-hungry. Checking a login recomputes the hash with the stored salt and compares the two in constant time.
python password-hash-demo.py salted
alice scrypt$131072$8$1$a7b59e514687af0791836e882ec36e5e$68406f17618695038c0b63b5963288147d034f07d6427adc7573b0683fe0c9df bob scrypt$131072$8$1$3ba2eceffb43af353b1bfacc2d563021$7660b0d06d231cd9e673305742df1bc3042d33f8beaf1c9180415681c07ea4c9 alice, correct input : True alice, wrong input : False Same input, different stored values. Each record carries its own random 16-byte salt, and the cost settings, so the format can be upgraded later without breaking old records.
03 — The cost
How fast is each method?
The same script times each method on this machine. The point is not the exact numbers, which depend on the computer, but the gap between them.
python password-hash-demo.py speed
MD5 2,380,385.3 per second SHA-256 1,933,786.5 per second PBKDF2-SHA256, 600,000 rounds 10.4 per second scrypt, N=131072, r=8, p=1 2.8 per second On this machine MD5 is 856,577 times faster than scrypt. A login takes a fraction of a second either way. Anyone who has to test guesses pays that cost for every single guess.
04 — Result
What to use instead
Fast hashes are built to be fast. MD5 and SHA-256 are great for checking files, as in Lab 01, and wrong for passwords, because speed helps whoever is testing guesses, not the defender. A password hash should be slow and memory-hungry on purpose.
- First choice: Argon2id. Use the library for your language, such as the argon2 package for Node.js, and keep its recommended settings.
- Also good: scrypt (as above) or bcrypt with a cost of 10 or more. Note that bcrypt only reads the first 72 bytes of the input.
- If compliance requires it: PBKDF2-HMAC-SHA256 with at least 600,000 rounds.
- Never: MD5, SHA-1, or a single round of SHA-256, with or without a salt.
- Always: A unique random salt per user, stored with the hash. Compare in constant time. Store the algorithm and cost settings so you can upgrade later.
- Around the hash: Rate-limit and lock repeated failures, refuse passwords found in known breach lists, and offer multi-factor authentication.
05 — Run it yourself
Same script, your machine
Download the script and run the three commands above with Python 3.8 or newer. Use python3 on Linux and macOS. The salts are random, so your stored values will differ from mine, and the timings depend on your computer. The gap between the methods will not.