Remove adsMember
Theme: SystemContrast: Standard
On this page
  1. Entropy belongs to how you chose, not to what you got
  2. How fast offline cracking is
  3. Why passphrases work
  4. Strength meters
  5. What NIST actually says
  6. What I’d do

Why length beats composition rules in passwords

By 9 min readExpert
Copy article
Share this article

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:

python
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.

Entropy gained by adding one more unit: widening the alphabet versus one more letter, symbol or word

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 tool

Share this article

Share this article

Sources

  1. NIST SP 800-63B-4: Digital Identity Guidelines, Authentication and Authenticator Management1
  2. OWASP Password Storage Cheat Sheet1
  3. RFC 9106: Argon2, the memory-hard function for password hashing1
  4. RFC 8018: PKCS #5 password-based cryptography (PBKDF2)1
  5. zxcvbn: Low-Budget Password Strength Estimation (USENIX Security 2016)1
  6. xkcd 936: Password Strength (Randall Munroe)1

Keyboard shortcuts

Close

Everywhere

Search tools and actions
Ctrl K
Show this help
?
Switch theme
Ctrl Shift L
Pin the selected tool (in search)
Ctrl P

On a tool

Send the output to the first suggestion
Ctrl ↵
Open a file into the input
Ctrl O
Copy the output
Ctrl Shift C
Close searchesc
toolsBase64 encoder and decoderEncode text to Base64 or decode Base64 back to text, with standard and URL-safe alphabets.EncodingJSON formatter and validatorFormat, minify and validate JSON, with the exact line and column of any error.FormattingRegex testerTest JavaScript regular expressions with live highlighting, capture groups and a plain-language explanation.TextUUID and ULID generatorGenerate UUID v4, UUID v7 and ULID in bulk, or paste an ID to see its version, variant and timestamp.GeneratorsUnix timestamp converterConvert Unix timestamps to dates and back, in any time zone, with ISO 8601, RFC 2822 and relative time.Date and timeHash and HMAC generatorCalculate MD5, SHA-1, SHA-2, SHA-3, BLAKE2, BLAKE3 and CRC-32 hashes of text or a file, with optional HMAC.SecurityPassword generator and strength checkerGenerate random passwords, passphrases and PINs, or check how strong a password is.SecurityJWT decoder and verifierDecode a JSON Web Token, check its expiry and claims, and verify its signature with a secret or public key.SecurityCIDR and IP subnet calculatorCalculate the range, mask and size of IPv4 and IPv6 networks, split them into subnets, and check what contains or overlaps what.NetworkCron expression explainerRead a cron expression in plain language, see its next runs in any time zone, and tidy it into its standard form.Date and timeSort linesSort lines of text A to Z or Z to A, in natural order (file2 before file10), by code point, by number or by length.TextRemove duplicate linesRemove duplicate lines from a list, keep the first or last copy, ignore case or spaces, or drop every line that repeats.TextCompare lists and textCompare two lists line by line: find what is in both, only in one, or in either, or see a marked line diff side by side.TextText case converter and slugifyConvert text to camelCase, snake_case, kebab-case, Title Case and more, or turn a title into a URL slug with accents handled.TextURL encode, decode and parsePercent-encode and decode text for URLs, or take a URL apart into scheme, host, path and query and edit the query as a table.EncodingValidators: IBAN, BSN, VAT, card and ISBNCheck an IBAN, Dutch BSN, credit card number, EU VAT number, email address, URL or ISBN, with the reason when it fails. One value per line.ValidationHTML entity encoder and decoderEscape text for HTML with named, decimal or hex entities, or turn entities such as & and é back into characters.EncodingEscape and unescape stringsEscape text for a JSON or JavaScript string, a SQL string, a CSV field, a regular expression or a shell word, or undo the escaping.EncodingEAN, UPC and GTIN check digitsCheck the check digit of an EAN-8, EAN-13, UPC-A or GTIN-14, add a missing one, read the country from the GS1 prefix and convert between ISBN-10 and EAN-13. One code per line.ValidationTest data generatorGenerate valid-looking test IBANs, BSNs, EAN barcodes and EU VAT numbers in bulk. They pass the checks, but belong to no one.GeneratorsSQL formatter and minifierFormat SQL so each clause sits on its own line, or minify it onto one line, for MySQL, PostgreSQL, SQLite, SQL Server and BigQuery.FormattingNumber base converterConvert numbers between binary, octal, decimal, hex and any base from 2 to 36, with big numbers, two's complement and a bit view.EncodingYAML, TOML and JSON converterConvert between JSON, YAML and TOML, or validate and pretty-print a document, with the line and column of every error.FormattingCSV and JSON converterConvert CSV to a JSON array of objects or arrays, or JSON back to CSV. Detects the delimiter and reads Dutch Excel files with semicolons and decimal commas.DataGlob and .gitignore testerTest glob patterns and .gitignore rules against a list of paths and see which paths match and which rule matched them.TextColour converterConvert a colour between HEX, RGB, HSL, HWB and OKLCH, check its WCAG contrast against a background, or build a Material 3 tonal palette from it.Formattingchmod calculatorConvert Unix file permissions between octal (755), letters (rwxr-xr-x), ls -l and chmod clauses, with a checkbox grid and the setuid, setgid and sticky bits.SecurityUser-agent parserRead a user-agent string: browser, engine, OS, device type and whether it is a bot. Leave it empty to read your own browser.NetworkConvert cURL to codePaste a cURL command and get the same request as fetch, Axios, Python requests, Go or PHP. Nothing is run and nothing is sent.NetworkMarkdown previewWrite Markdown and see it rendered as you type, or copy the HTML. Raw HTML is shown as text and links are checked, so nothing in the preview can run.FormattingBarcode and QR code generatorMake a QR code, EAN-13, UPC-A, Code 128, ITF-14 or Data Matrix barcode, download it as SVG or PNG, or read a code from an image.GeneratorspagesChangelogWhat changed in each releasepagesAboutWhat codeknife.dev is and who makes itpagesIdeasSuggest improvements and vote on other visitors’ ideas.pagesPrivacyWhat leaves your browser and what is storedpagesAccessibilityConformance, known limitations and how to report a problempagesMembershipRemove ads and raise the limitspagesactions