Password Entropy: Why Length Beats Complexity Every Time
Ask a developer to generate a strong password and most will reach for a jumble of symbols: P@ssw0rd! or something similarly unmemorable. The intuition is that complexity is what makes a password hard to crack. It is not. Length is. This post works through the actual math so you can stop guessing and start sizing passwords against a threat model.
The only metric that matters: entropy
Password strength is measured in bits of entropy — the base-2 logarithm of the size of the space an attacker must search. For a password of length L drawn uniformly from an alphabet of N characters:
H = L * log2(N) The exponent is the key. Every additional bit doubles the work of a brute-force attack. Every additional character from a 26-letter alphabet adds about log2(26) = 4.7 bits. Complexity adds a smaller alphabet boost; length multiplies it.
Worked example: complexity vs length
Compare two passwords:
- 8 chars, all 94 printable ASCII:
H = 8 * log2(94) = 8 * 6.55 = 52.4 bits - 20 chars, lowercase only (26):
H = 20 * log2(26) = 20 * 4.70 = 94.0 bits
The long, all-lowercase passphrase has roughly 2^41 times more search space than the short "complex" one. That is the entire argument. A 20-character phrase of ordinary words beats an 8-character symbol soup by an absurd margin, and it is easier to remember.
Where the model breaks down
The formula assumes characters are chosen independently and uniformly. Real passwords violate this in three ways:
- Humans are not random. "correct horse battery staple" is 25 characters but its entropy is nowhere near 25 × 4.7 bits — it is a handful of dictionary words, and dictionary attacks exploit exactly that.
- Password reuse collapses everything. If one site leaks, every site using that credential is compromised. Entropy per password does not help if the same password is used everywhere.
- Generated ≠ random. A generator that uses
Math.random()instead of a cryptographically secure source (CSPRNG) produces output that looks random but is predictable. Always usecrypto.getRandomValuesin the browser orsecretsin Python.
This is why the practical advice is: use a generator with a CSPRNG, make it long (16+ characters), and use a unique one per site — ideally stored in a password manager so you never have to remember any of them.
A quick entropy reference
| Alphabet | Bits per character | 12 chars | 16 chars | 20 chars |
|---|---|---|---|---|
| lowercase (26) | 4.70 | 56 | 75 | 94 |
| lower+upper (52) | 5.70 | 68 | 91 | 114 |
| alnum (62) | 5.95 | 71 | 95 | 119 |
| all printable (94) | 6.55 | 79 | 105 | 131 |
Note how the 16-character "lowercase only" row (75 bits) already clears the short "all printable" row (79 bits at 12 chars is higher — but at 8 chars it is only 52). At a fixed length, a bigger alphabet helps; across lengths, adding characters wins faster.
Tooling
When you need to check the exact entropy of a candidate or produce a CSPRNG-backed password with configurable length and character sets, I built a small client-side tool: the Random Password Generator. It runs entirely in the browser, exposes length and character-type controls, can exclude ambiguous characters (0/O, 1/l), and shows the resulting entropy so you can see the tradeoff rather than guess. No passwords are transmitted anywhere.
Bottom line
- Maximize length first, alphabet size second.
- Use a CSPRNG, never
Math.random(). - One unique password per site, via a manager.
- 16+ characters is a sane default for anything important.
Entropy is just arithmetic. Once you internalize H = L * log2(N), the "complex vs long" debate stops being a matter of opinion.