Password Security Best Practices for Developers
Passwords remain the most common authentication mechanism on the internet. Despite decades of alternatives — biometrics, hardware keys, passkeys — most systems still rely on passwords as their primary or fallback authentication method. Getting password security right protects your users; getting it wrong makes headlines.
This guide covers password generation, storage, and validation from a developer's perspective.
Password Entropy
Entropy measures how unpredictable a password is. Higher entropy means more guesses required to crack it. Entropy is measured in bits:
Entropy = log2(possible_characters ^ length)
| Password type | Characters | Length | Entropy |
|---|---|---|---|
| 4-digit PIN | 10 | 4 | 13 bits |
| 8 lowercase letters | 26 | 8 | 38 bits |
| 8 mixed case + digits | 62 | 8 | 48 bits |
| 12 mixed + symbols | 95 | 12 | 79 bits |
| 16 mixed + symbols | 95 | 16 | 105 bits |
| 4 random words (diceware) | 7,776 | 4 | 51 bits |
| 6 random words (diceware) | 7,776 | 6 | 78 bits |
Minimum recommendation: 72+ bits of entropy for important accounts. This means at least 12 characters with mixed case, digits, and symbols — or 6 random words.
Generating Strong Passwords
What makes a password strong?
- Length — the single most important factor. Each additional character multiplies the search space
- Randomness — must be generated by a cryptographic random source, not chosen by a human
- Uniqueness — one password per service, never reused
Why humans are bad at passwords
Humans choose predictable patterns: dictionary words, dates, keyboard patterns (qwerty), substitutions (p@ssw0rd). These patterns are in every password cracker's dictionary. A "clever" human-chosen password like Tr0ub4dor&3 has about 28 bits of entropy — far less than a random 12-character string.
Cryptographic randomness
Password generators must use a cryptographic random number generator (CSPRNG):
- Browser:
crypto.getRandomValues() - Node.js:
crypto.randomBytes() - Python:
secrets.token_hex() - Linux:
/dev/urandom
Never use Math.random() or language-level pseudo-random generators — they are predictable.
Storing Passwords (Server-Side)
Never store plaintext passwords
This should be obvious, but breaches at major companies prove it is not. Never store passwords in plaintext, Base64, or reversible encryption.
Use a password hashing function
Password hashing functions are designed to be slow — they take 100ms+ per hash, making brute-force attacks impractical:
| Algorithm | Recommended? | Notes |
|---|---|---|
| Argon2id | Best choice | Winner of the Password Hashing Competition. Memory-hard, resistant to GPU and ASIC attacks |
| bcrypt | Good | Widely available, CPU-hard. 72-byte input limit |
| scrypt | Good | Memory-hard and CPU-hard |
| PBKDF2 | Acceptable | Available everywhere but GPU-parallelizable. Use with SHA-256 and high iteration count |
| MD5 | Never | Fast hash, trivially cracked |
| SHA-256 | Never | Fast hash, not designed for passwords |
Always salt passwords
A salt is a random value added to each password before hashing:
hash = argon2id(password + salt)
store: salt + hash
Salting prevents:
- Rainbow tables — precomputed hash lookups
- Identical hashes — two users with the same password get different hashes
Most password hashing libraries handle salting automatically (bcrypt, Argon2id include the salt in the output).
Configuration recommendations
Argon2id: memory=64MB, iterations=3, parallelism=1 (adjust based on your server's resources)
bcrypt: cost factor=12 (adjust upward as hardware improves)
PBKDF2: iterations=600,000+ with SHA-256
Password Validation Rules
Do
- Require a minimum length (12+ characters recommended by NIST SP 800-63B)
- Check against a list of known breached passwords (the Have I Been Pwned API provides this)
- Allow all printable Unicode characters, including spaces
- Allow passwords up to at least 128 characters
Do not
- Require specific character classes (uppercase, digit, symbol) — NIST recommends against this
- Impose a maximum length under 64 characters
- Force periodic password changes — NIST found this leads to weaker passwords
- Use password hints or security questions — these leak information
NIST's research shows that composition rules (must include uppercase, digit, symbol) lead to predictable patterns like Password1!. Length and randomness are more effective than complexity rules.
Common Mistakes
- Logging passwords — never log request bodies that contain passwords, even in error logs
- Comparing with
==— use constant-time comparison to prevent timing attacks - Rolling your own crypto — use established libraries (bcrypt, Argon2id) rather than implementing hashing yourself
- Rate limiting failures — without rate limiting, an attacker can brute-force the login endpoint
- Password in URL — query parameters appear in server logs, browser history, and referrer headers
Generate Passwords
StackCache Password Generator creates strong passwords with cryptographic browser randomness. Configure length, character classes, and exclusion rules — everything runs locally with no server.
Try it yourself
Open the tool mentioned in this guide — it runs locally in your browser, no account needed.
Open tool