Your password policy requires: minimum 8 characters, at least one uppercase, one lowercase, one digit, and one special character. A user enters P@ssw0rd123. It passes validation. It's crackable in under 3 seconds by any modern offline attack tool. Your policy produces predictable patterns — people follow the rules with the minimum effort, and attackers know that.
Here's the math on why this happens and what password requirements actually work.
Entropy: The Only Number That Matters
Password strength comes down to one quantity: entropy, measured in bits. Entropy = log₂(character_space ^ password_length).
For your 8-character complexity policy:
- Uppercase: 26 characters
- Lowercase: 26 characters
- Digits: 10 characters
- Special characters: ~32 common ones
- Total character space: ~94 characters
Entropy: log₂(94^8) = 52 bits
That sounds like a lot. But an offline attacker with a modern GPU rig (8× RTX 4090s) can test 200 billion MD5 hashes per second. At that rate, 52 bits of entropy is exhausted in 23 seconds in a brute-force attack.
With bcrypt (cost factor 12, about 350ms per hash on that hardware), the same 52-bit space would take 8.3 years. This is why the hashing algorithm matters enormously — but so does entropy.
Why Complexity Rules Backfire
Complexity rules don't produce random passwords — they produce predictable human behavior:
P@ssw0rd(capital first, leet substitutions)Summer2026!(season + year + required special)Company1!(company name + number + required special)
In practice, passwords meeting typical complexity rules cluster around a small subset of patterns. Breach database analysis consistently shows that tens of millions of distinct "complex" passwords fit fewer than 100 common templates. Attackers use rule-based cracking (Hashcat's rule engine) to test all templates systematically.
The result: a password that passes your complexity requirements and fails against a competent offline attack in minutes.
What Actually Works: Length and Randomness
A 4-word random passphrase beats a complex 8-character password:
Four random words from a 7,776-word wordlist (Diceware):
- Character space: 7,776 possible words per position
- Length: 4 words
- Entropy: log₂(7,776^4) = 51.7 bits — similar to the complex password
But a 5-word passphrase:
- Entropy: log₂(7,776^5) = 64.6 bits
And a 6-word passphrase:
- Entropy: log₂(7,776^6) = 77.5 bits
At bcrypt cost factor 12, 77.5 bits of entropy (a 6-word passphrase like correct-horse-battery-staple-river-fog) is uncrackable — the heat death of the universe comes first. At the same time, it's memorable. This is why NIST SP 800-63B (updated 2024) recommends enforcing minimum length (12+ characters) rather than character composition rules.
Bcrypt Cost Factor Recommendations
Bcrypt's cost parameter determines how many iterations run — each increment doubles the computation time. At cost 12, hashing takes ~250-350ms on server hardware (2026 specs). That's acceptable for login latency; it makes offline attacks prohibitive.
| Cost | Time per hash (2026 server) | GPU attack speed (8× RTX 4090) |
|---|---|---|
| 10 | ~65ms | ~7M hashes/sec |
| 12 | ~250ms | ~1.8M hashes/sec |
| 13 | ~500ms | ~900K hashes/sec |
| 14 | ~1,000ms | ~450K hashes/sec |
Recommendation: cost 12 for active login flows. The 250ms latency is imperceptible to users; the 1.8M hashes/sec attack rate means a 64-bit entropy password survives an offline attack for approximately 324,000 years.
Never use MD5 or SHA-1 (unsalted) for passwords. Both are fast hashing algorithms designed for data integrity, not password storage. An attacker with a stolen MD5-hashed password database and a modern GPU can crack every 8-character password in minutes using precomputed rainbow tables.
Breach Database Checking (HIBP API)
NIST now recommends checking new passwords against breach databases. The Have I Been Pwned (HIBP) API lets you do this without sending the actual password:
- Hash the password with SHA-1
- Send the first 5 characters of the hash to
https://api.pwnedpasswords.com/range/{first5chars} - The API returns all hash suffixes that match
- Check if your hash's suffix is in the response
This "k-anonymity" model means HIBP never sees the full hash. If the password appears in 847 breach records, reject it with an explanation — don't just say "password too weak," tell the user this specific password was found in data breaches.
The Practical Policy
For new systems (NIST SP 800-63B aligned):
- Minimum 12 characters
- No composition rules (no mandatory uppercase/special character)
- Check against breach databases on signup and password change
- Maximum length of at least 64 characters (support passphrases)
- bcrypt with cost 12 (upgrade to 13 when your servers can absorb the latency)
For legacy systems with complexity rules:
- Increase minimum length to 16 characters (this alone doubles entropy from 52 to ~103 bits for typical user-chosen passwords)
- Keep composition rules if removing them requires too much UI change
- Add breach database checking — this catches the worst passwords that satisfy your rules
Argon2 as the Modern Alternative to bcrypt
bcrypt was the standard recommendation for a decade and remains secure. However, argon2id (winner of the 2015 Password Hashing Competition) is the current best practice recommendation from OWASP and NIST as of 2026.
Argon2id's advantage over bcrypt: it's configurable in memory usage as well as time. An attacker with specialized hardware (ASICs or FPGAs) can run bcrypt fast by throwing silicon at the problem. Argon2id's memory parameter (default recommendation: 64MB) prevents hardware acceleration because GPU RAM is expensive and limited. An attacker needs 64MB per hash attempt — limiting parallelism even with high-end hardware.
OWASP's 2026 recommendation: argon2id with 64MB memory, 3 iterations, parallelism factor 4. If you're starting a new system, use argon2id. If you're on an existing bcrypt system, bcrypt at cost 12 is still acceptable — don't migrate just for the sake of it.
The Password Generator produces cryptographically random passwords that aren't susceptible to pattern attacks. For developer secrets, API keys, and JWT signing secrets, always use random generation — never type them.
Password Generator
Generate cryptographically secure passwords with configurable length and character sets.