Why the randomness source matters more than the character set
Math.random() is a fast pseudo-random generator seeded from a small internal state. It is not designed to be unpredictable, and in V8 an attacker who observes a handful of outputs can recover the state and predict every subsequent value. A password generator built on it produces strings that *look* random and are not. crypto.getRandomValues() is a different thing entirely: it draws from the platform CSPRNG (/dev/urandom on Linux, BCryptGenRandom on Windows), which is designed so that seeing past output tells you nothing about future output.
There is a second, subtler correctness issue that most generators get wrong: modulo bias. Taking a random byte and computing byte % 62 makes the first few characters of the alphabet more likely than the rest, because 256 does not divide evenly by 62. Over a long password the skew is measurable and reduces real entropy. This tool uses rejection sampling — it draws a 32-bit value, discards any that fall outside the largest exact multiple of the alphabet size, and retries — so every character is exactly equally likely.
Reading the entropy figure
Entropy for a uniformly random string is length × log₂(alphabet size). A 16-character password from the full 88-character set here is 16 × log₂(88) ≈ 103 bits. That number is the log of how many equally likely passwords could have been produced — 2¹⁰³ of them — which is the only figure that matters for offline cracking.
Read the full explanation of password generator results ↓Show less ↑
| Entropy | Search space | Realistic verdict |
|---|---|---|
| < 40 bits | ~10¹² | Cracked in seconds against a fast hash. Not usable. |
| 50–60 bits | ~10¹⁷ | Fine behind a rate-limited login, weak against an offline leak. |
| 70–80 bits | ~10²³ | Comfortable for most accounts, including against a leaked bcrypt hash. |
| 100+ bits | ~10³⁰ | Not brute-forceable with any foreseeable hardware. Use for root, key material, and long-lived secrets. |
Two caveats keep this honest. First, entropy only describes randomly generated strings — a human-chosen password with the same character mix has far less, because humans concentrate their choices in a tiny corner of the space. P@ssw0rd! scores 59 bits on a naive character-class calculator and is in every cracking wordlist ever assembled. Second, the "guarantee one from each set" option removes a small number of possible strings, so the true entropy is a fraction of a bit below the displayed figure. At these lengths that is not decision-relevant, but it is not zero either.
How fast entropy is consumed depends entirely on how the target stores the password. Against unsalted SHA-1 a commodity GPU rig tries tens of billions of candidates per second; against bcrypt at cost 12 it manages a few tens of thousands. That is a factor of roughly a million, which is worth about 20 bits of password length — which is why choosing the storage algorithm is at least as important as choosing the password. The hash generator shows what those digests look like, and the htpasswd generator covers picking a storage scheme for Basic auth.
Choosing length and character sets
Length buys entropy faster than exotic characters do. Going from 12 to 16 characters over a 62-character alphabet adds about 24 bits; adding the full symbol set to a 12-character password adds about 5. If a system limits you to letters and digits, just make it longer rather than fighting the restriction.
- Web accounts stored in a password manager: 20+ characters, all sets on. You never type it, so complexity costs nothing.
- Something you must type on a phone or read aloud: 24+ characters, letters and digits only, look-alikes excluded. Length compensates for the smaller alphabet, and you avoid the "is that a 1 or an l" support call.
- Database, API, and service credentials: 32+ characters. Watch for systems that silently truncate — bcrypt ignores everything past 72 *bytes*, and some MySQL and legacy appliance configurations cut off much earlier.
- Anything in a URL, connection string, or environment file: avoid symbols that need escaping.
@ : / ? # &all have meaning inside a connection URI, and a password containing them will break parsing in a way that produces a confusing error rather than an auth failure. See the URL encoder if you must keep them.
The "exclude look-alike characters" option removes 0 O o, 1 l I |, 5 S, 2 Z, 8 B and 6 G. It costs about 0.3 bits per character — a 20-character password drops from roughly 130 to 123 bits — and it eliminates an entire category of transcription failure. Turn it on whenever a human will read the password off a screen.
Generating passwords on the command line
On a server you usually want this without a browser. All of the following read from the kernel CSPRNG, which is the same source this page uses.
# 32 URL-safe characters from /dev/urandom via openssl (~190 bits)
openssl rand -base64 24 | tr -d '\n'
# Hex, when the consumer is picky about the alphabet
openssl rand -hex 32
# Filter /dev/urandom to a chosen alphabet — 20 chars, no look-alikes
LC_ALL=C tr -dc 'A-HJ-NP-Za-km-z3-9' < /dev/urandom | head -c 20; echo
# A diceware-style passphrase you can actually remember and type
shuf -n 5 --random-source=/dev/urandom /usr/share/dict/words | paste -sd- -
# Never do this: $RANDOM is a 15-bit LCG, not a CSPRNG
# echo $RANDOM$RANDOM <-- ~30 bits total, cracked instantlyThe shuf line is worth understanding. Five words drawn uniformly from a 235,000-word dictionary is about 87 bits — stronger than most 12-character random strings, and typeable by a human. The strength comes entirely from the words being chosen *by the machine*; a passphrase you invent yourself is worth a fraction of that, because you will unconsciously pick words that go together.
What to do with the password once you have it
Put it in a password manager immediately, before you close the tab. The whole value proposition of a 100-bit random string is that it is unique to one account and never reused; that only works if something else remembers it for you. Reuse is the failure mode that actually causes breaches — credential-stuffing attacks take passwords leaked from a site with weak storage and try them everywhere else, and a strong password reused across ten sites is exactly as weak as the worst of those ten.
Do not paste it into a chat, a ticket, or an email to "send it over". Those systems retain content indefinitely and are searchable by more people than you think. If you must transfer a credential, use a one-time secret link with an expiry, or send the password over a different channel from the username. And rotate anything that has ever appeared in a shell history file, a CI log, or a git commit — assume it is public the moment it lands there.
Finally: password strength does nothing against phishing, session-token theft, or a keylogger. Multi-factor authentication is what covers those. A 128-bit password and no second factor is a worse position than a 70-bit password and a hardware key.