Why length beats composition rules in passwords
A signup form wants one uppercase letter, one digit and one symbol. It accepts Password1! without complaint. That password ticks every box and falls in the first second of any cracking run, because every cracking dictionary has it, decoration and all. Meanwhile plenty of forms reject correct horse battery staple (yes, the xkcd one) for not having a digit.
That’s backwards, and the numbers show why. You can check all of them yourself.
Entropy belongs to how you chose, not to what you got
Draw a password uniformly at random from N symbols, L times. There are N^L equally likely outcomes, and the entropy is
H = L * log2(N) bits
On average the attacker needs 2^(H-1) guesses, half the space. Per symbol:
| Alphabet | N | bits per symbol |
|---|---|---|
| Digits | 10 | 3.32 |
| Lowercase letters | 26 | 4.70 |
| Mixed case + digits | 62 | 5.95 |
| Printable ASCII | 94 | 6.55 |
| Diceware word list (7776 words) | 7776 | 12.92 |
That table alone sinks composition rules. Going from lowercase-only (4.70 bits) to all of printable ASCII (6.55) gains 1.85 bits per character. One extra lowercase character gains 4.70. Ten random lowercase letters (47.0 bits) beat seven random printable characters (45.9 bits). Length sits in the exponent. The alphabet only gets a logarithm.
The catch is the word random. Entropy measures how the password was picked, not what it looks like. Tr0ub4dor&3 looks like 11 mixed characters, about 72 bits if you count naively (11 * 6.55). It’s really a dictionary word with a predictable capital, predictable substitutions and a predictable suffix, and a rule-based cracker covers that whole family in a tiny slice of the naive space. People are bad random number generators, and attackers model exactly how bad.
So any entropy figure you compute from the characters of a human-invented password is an upper bound, at best. The honest number is the entropy left after the attacker’s best model of how people pick passwords, and that’s much lower.
How fast offline cracking is
Online guessing, typing into a login form, runs into rate limits and lockouts. Thousands of guesses a day is about right. That’s not the case to worry about. Worry about the offline one: the database leaked, the attacker has the hashes, and nothing stands between them and their own GPUs.
Offline, the guess rate depends entirely on the hash. These are orders of magnitude; real rates vary by GPU and cost parameters, so run hashcat -b on hardware you care about instead of trusting a table. This one included.
| Hash | Order of magnitude on a single high-end GPU | Why |
|---|---|---|
| MD5, NTLM | 10^10 to 10^11 guesses/s | Tiny state, no work factor |
| SHA-1, SHA-256 (one round) | about 10^10 /s | Designed to be fast |
| PBKDF2-HMAC-SHA256 [4], 600,000 iterations | about 10^4 /s | Iterations divide the rate directly |
| bcrypt, cost 12 | about 10^3 /s | 4 KB state, hard to parallelise on GPUs |
| Argon2id [3], memory-hard parameters | 10^2 to 10^3 /s | Needs memory per guess, which GPUs have little of per core |
Seven to nine orders of magnitude between the top row and the bottom. That gap is why “use a slow, salted hash” matters more than nearly any password-policy argument.
In time, with 2^(H-1) guesses on average:
def avg_seconds(bits, guesses_per_s):
return 2 ** (bits - 1) / guesses_per_s
for bits in (40, 50, 60, 77.5):
fast = avg_seconds(bits, 1e10) # fast hash, one GPU
slow = avg_seconds(bits, 1e3) # bcrypt-class, one GPU
print(f"{bits:5} bits fast: {fast/86400:12.4g} days slow: {slow/86400/365:12.4g} years")Against a fast hash at 10^10 guesses a second, 40 bits lasts about 55 seconds on average. 50 bits, about 15 hours of one GPU. 60 bits, around 1.8 years of one GPU, which a rented cluster of a few hundred finishes in days. Against bcrypt-class hashing at 10^3 a second, 40 bits takes about 17 years of one GPU and 50 bits about 18,000.
Attackers don’t have one GPU, though, and their speed scales linearly with money. Budget a factor of 10^3 to 10^4 into your threat model and see what’s left of your margin.
Also: your real strength is the weakest hash that ever stored the password. Including that one legacy table nobody got round to migrating.
Why passphrases work
The comic that made this argument famous is xkcd 936, “Password Strength” [6], by Randall Munroe. Open it in another tab. It does the entropy maths in two panels and the numbers hold up.
It puts Tr0ub4dor&3 at about 28 bits. It counts the choices a person actually makes: one uncommon base word (about 16 bits), whether to capitalise it, the usual substitutions, the punctuation, the digit, and the order. Every one of those habits is predictable, so the total stays small despite the symbols. 2^28 guesses at 1000 a second is about 3 days.
Four random common words come to about 44 bits: 4 words at 11 bits each, assuming a list of about 2048 words. 2^44 at 1000 guesses a second is about 550 years.
Two things to keep in mind. The 1000 guesses a second is an online attack on a weak remote service, and the comic says so. After a leak the attacker works offline and the same bits fall much faster (see the table above), so the comic’s ranking survives but its times don’t apply to a stolen hash. And the 44 bits only exist if the words really are random, picked with dice or a CSPRNG. Words that “felt random” aren’t.
With a 7776-word list it gets a bit better. Six Diceware words give 6 * 12.92 = 77.5 bits, as long as they’re chosen by dice or a CSPRNG from a known, uniform list. The average search is 2^76.5 guesses. At 10^11 guesses a second on one GPU that’s about 1.1 x 10^12 seconds, roughly 34,000 years. A thousand of those GPUs get it down to about 34 years, which is why the hash cost still matters on top. And you can actually type and remember six words. A 12-character random string of 6.55-bit symbols (78.6 bits) is about as strong, and nobody remembers that.
Memorability is a length problem, and people are good at remembering sequences of familiar chunks. A word is a chunk worth 12.9 bits. A passphrase swaps a big alphabet (a dictionary) for fewer symbols, and the arithmetic favours you.
It fails the same way as before. Four words you picked because they felt random aren’t 4 * 12.9 bits. Lyrics, quotes and “my dog’s name plus my street” are all in attackers’ phrase lists. Four dice-picked words is 51.7 bits: fine behind an expensive hash, thin behind a fast one. Five is 64.6, six is 77.5.


Strength meters
A meter has to estimate how many guesses a competent attacker would need without knowing the attacker. The naive kind counts character classes and length, which is the composition-rule heuristic with a progress bar on it. It’ll happily call Password1! strong.
Better estimators like zxcvbn [5] model the attacker instead. They match substrings against ranked dictionaries (common passwords, names, words, years), keyboard walks, repeats, sequences and l33t substitutions, find the cheapest way to break the password down, and report an estimated guess count. Much better.
They still can’t know what’s in real breach corpora; attackers build wordlists from actual leaks and the meter ships a frozen subset. They can’t see reuse: a 90-bit password reused on a site that stored it in plaintext is worth zero bits. They don’t know which hash will store it, and the same guess count means very different times on MD5 and Argon2id. And they can’t see how the password was generated, so a random 12-character string and a clever human pattern can score the same.
Use a meter to reject the obviously bad, not to certify the good. The password generator is handy for exactly that: playing with the arithmetic above and checking a candidate for length and pattern weaknesses. Just don’t read its verdict as a guarantee, and don’t paste a password you actually use into anyone’s strength checker. Test the idea with throwaway examples.
What NIST actually says
NIST’s password guidance is probably the most misquoted document in this field. The current revision is SP 800-63-4, published August 2025, superseding 800-63-3 [1]. Older material, and 800-63-3 itself, gives a flat 8-character floor; the new revision splits that by how the password is used. It’s blunt, and it throws out decades of folk wisdom. Paraphrasing the verifier requirements:
| Topic | What it says | Folk wisdom it replaces |
|---|---|---|
| Minimum length | At least 15 characters when the password is the only factor; 8 is the minimum only when it is one factor of a multi-factor login. Verifiers should permit at least 64 | “8 with a symbol” and a low maximum |
| Composition rules | Must not be imposed (no mandatory upper, digit, symbol mix) | Complexity rules |
| Periodic rotation | Must not be required; change only on evidence of compromise | 90-day expiry |
| Blocklist | New passwords must be checked against a blocklist of commonly used, expected or compromised values | Nothing, or composition rules instead |
| Paste and managers | Paste should be permitted; password managers and autofill must be allowed | Paste blocking |
| Truncation | The whole password must be verified without truncation (trimming leading and trailing whitespace is allowed) | Silent cut-off at N chars |
| Storage | Salted, with an approved key-derivation function and a cost factor | Plain or fast hashes |
NIST’s reasoning is empirical. Composition rules push people toward predictable transformations (capital first letter, 1! on the end). Forced rotation pushes them toward predictable increments (Summer2026, then Autumn2026). Both lower real entropy while making it look higher.
Note too that it’s guidance for the verifier, the service. For users it boils down to: long, unique, preferably random and generated by a manager, plus a second factor. The 15-character floor is a floor for a system with rate limiting and a blocklist, not advice on what to pick. An offline attacker is outside that model entirely, which is where hash parameters come in. For storage, OWASP’s cheat sheet [2] is the practical companion: Argon2id first, with concrete minimum parameters, and scrypt, bcrypt or PBKDF2 where those are what you’ve got. RFC 9106 specifies Argon2; RFC 8018 specifies PBKDF2.
What I’d do
Let a password manager generate everything it can. A random 20-character string costs you nothing to remember, because you don’t remember it.
For the few secrets you must remember (the manager’s master password, disk encryption), roll a six-word Diceware passphrase with real dice or a CSPRNG. About 77.5 bits.
Never reuse. Reuse drags every account down to the storage of the worst site you ever signed up for.
If you run the server: Argon2id, or bcrypt or PBKDF2 with a high iteration count if you must, a unique salt per user, tuned so one verification costs between 100 ms and a second on your hardware. Re-tune as hardware gets faster. Drop the composition rules and the 90-day expiry; require 15 characters for single-factor passwords, check against a blocklist of compromised passwords, and rate-limit.
And add a second factor. Entropy stops guessing. It does nothing about phishing or a keylogger.
Try it yourself
Password generator and strength checker
Generate random passwords, passphrases and PINs, or check how strong a password is.
Open the toolShare this article
Sources
- NIST SP 800-63B-4: Digital Identity Guidelines, Authentication and Authenticator Management1
- OWASP Password Storage Cheat Sheet1
- RFC 9106: Argon2, the memory-hard function for password hashing1
- RFC 8018: PKCS #5 password-based cryptography (PBKDF2)1
- zxcvbn: Low-Budget Password Strength Estimation (USENIX Security 2016)1
- xkcd 936: Password Strength (Randall Munroe)1