In 2024, "123456" was still the most common password worldwide. But even a strong password is only as secure as how it's stored. This guide covers password security from both sides — generating strong passwords and storing them safely as a developer.
Password Entropy: Measuring Strength
Entropy quantifies how unpredictable a password is, measured in bits:
Entropy = log₂(character_pool ^ length)
| Character Set | Pool Size | 8 chars | 12 chars | 16 chars |
|---|---|---|---|---|
| Lowercase only | 26 | 37 bits | 56 bits | 75 bits |
| Lower + Upper | 52 | 46 bits | 68 bits | 91 bits |
| Lower + Upper + Digits | 62 | 48 bits | 71 bits | 95 bits |
| All printable ASCII | 94 | 52 bits | 78 bits | 105 bits |
How Passwords Should Be Stored
Never store passwords in plain text or with reversible encryption. The correct approach is one-way hashing with a salt:
- Generate a unique random salt (16-32 bytes) for each password
- Hash:
stored_hash = hash_function(salt + password) - Store the salt and hash together (most algorithms handle this automatically)
- To verify: recompute the hash with the same salt and compare
Password Hashing Algorithms Compared
| Algorithm | CPU-Hard | Memory-Hard | Parallelism Resistance | Recommendation |
|---|---|---|---|---|
| Argon2id | ✅ | ✅ | ✅ | Best choice for new applications |
| bcrypt | ✅ | ❌ | Moderate | Excellent, widely supported |
| scrypt | ✅ | ✅ | ✅ | Good alternative to Argon2 |
| PBKDF2 | ✅ | ❌ | ❌ | NIST-approved, but vulnerable to GPU attacks |
| SHA-256 | ❌ | ❌ | ❌ | Never use for passwords |
| MD5 | ❌ | ❌ | ❌ | Broken — never use |
Why General-Purpose Hashes Fail for Passwords
SHA-256 can compute billions of hashes per second on a modern GPU. An 8-character password using all printable ASCII (52 bits of entropy) can be brute-forced in under 24 hours. Password-specific algorithms add a configurable work factor that makes each hash computation intentionally expensive:
- bcrypt with cost factor 12: ~250 ms per hash → brute-forcing 52-bit password takes centuries
- Argon2id with 64 MB memory, 3 iterations: ~500 ms per hash, plus requires 64 MB RAM per attempt
Salting: Why Every Password Needs Its Own
Without salts, identical passwords produce identical hashes. An attacker with a stolen database can:
- Use precomputed rainbow tables to instantly reverse common password hashes
- Identify users sharing the same password (they'll have the same hash)
- Crack one password and compromise all accounts using it
A unique salt per password defeats all of these attacks. Both bcrypt and Argon2 handle salting automatically — the salt is embedded in the output string.
Password Generation Best Practices
- Use a cryptographically secure random number generator —
crypto.getRandomValues()in browsers,secretsmodule in Python,SecureRandomin Java - Never use Math.random() for security purposes — it's not cryptographically secure
- Minimum 12 characters for standard accounts, 16+ for high-security
- Passphrases work — 4-5 random dictionary words provide both high entropy and memorability
- Avoid patterns — "Password1!" meets most complexity requirements but is trivially crackable
Common Password Security Mistakes
- Storing passwords in plain text — any database breach exposes all credentials
- Using MD5 or SHA-1 — both are computationally broken for password use
- Enforcing maximum length limits — if you're hashing, the input length doesn't affect storage
- Requiring frequent password changes — NIST no longer recommends this; it leads to weaker passwords
- Blocking paste in password fields — this discourages password manager use
- Rolling your own crypto — use established libraries (bcryptjs, argon2, passlib)