Password security is often misunderstood. This guide covers what actually determines password strength, how entropy works, and best practices for both users and developers.
What makes a password strong?
Two factors dominate: length and randomness.
Length has an exponential effect. Adding one character multiplies the search space by the size of the character pool. Doubling the length squares the difficulty.
Randomness means the password wasn't chosen by a human. Human-generated passwords follow predictable patterns: dictionary words, names, dates, keyboard walks (qwerty, 123456). Attackers know this and target these patterns first.
A random password generator uses the browser's cryptographic random number generator (crypto.getRandomValues()) — not Math.random(), which is not cryptographically secure.
Password entropy
Entropy measures unpredictability in bits:
``
entropy = length × log₂(pool_size)
`
Pool sizes:
- Lowercase only (a-z): 26 characters
- Lowercase + uppercase: 52 characters
- Lowercase + uppercase + digits: 62 characters
- + symbols (the 32 printable special characters): 94 characters
Examples:
| Password | Pool | Length | Entropy | Strength |
|----------|------|--------|---------|----------|
| password | 26 | 8 | ~38 bits | Very weak |
| P@ssw0rd | 94 | 8 | ~52 bits | Still weak |
| Tr0ub4dor&3 | 94 | 11 | ~72 bits | Moderate |
| random 16-char, 94-pool | 94 | 16 | ~105 bits | Strong |
| random 20-char, 94-pool | 94 | 20 | ~131 bits | Very strong |
50–64 bits: very weak (crackable in hours/days)
70–84 bits: weak (crackable in months with powerful hardware)
90–100 bits: reasonable (years with current technology)
128+ bits: very strong (centuries even with significant resources)
The oft-cited P@ssw0rd substitutions (letter → number or symbol) add almost no entropy — attackers know this pattern and include it in their dictionaries.
The passphrase alternative
A passphrase from a large wordlist can achieve good entropy while being memorable:
<code>
<p>correct-horse-battery-staple</p>
</code>
From a 7,776-word Diceware wordlist:
- 4 words: log₂(7776⁴) ≈ 51 bits — borderline
- 5 words: ≈ 64 bits — reasonable
- 6 words: ≈ 77 bits — strong
- 7 words: ≈ 90 bits — very strong
Advantage: Memorable and typeable. Good for master passwords.
Disadvantage: Longer (more characters to type). Not ideal for short password fields.
How attackers crack passwords
Dictionary attacks: Try millions of common passwords and known breach passwords. Lists like rockyou.txt (14 million passwords from a 2009 breach) are standard starting points.
Rule-based attacks: Apply transformations to dictionary words: capitalize the first letter, add a number at the end, substitute letters with symbols. Password1! is instantly cracked this way.
Brute force: Try every combination. With modern GPUs, an 8-character password using lowercase only is cracked in minutes.
Rainbow tables: Precomputed hash→password tables. Defeated by salting (adding a random value to each password before hashing).
Credential stuffing: Use username/password pairs from previous data breaches. Countered by unique passwords per site.
The practical implication: any password that a human would generate, or that includes recognizable patterns, is vulnerable. True randomness is the only reliable protection.
Developer: password requirements
Do:
- Accept at minimum 8 characters (NIST SP 800-63B recommends supporting up to 64 characters)
- Accept all printable ASCII and Unicode characters
- Use a password strength meter (zxcvbn library is excellent)
- Use a breached password check (HaveIBeenPwned API)
Don't:
- Require specific character types (NIST no longer recommends mandatory complexity rules)
- Force periodic password rotation without a specific reason (leads to predictable changes)
- Truncate passwords
- Store passwords as MD5 or SHA-1 hashes — use bcrypt, Argon2id, or scrypt
- Limit password length to less than 64 characters
Password hashing in code:
`python
Python: Argon2id (recommended for new systems)
from argon2 import PasswordHasher
ph = PasswordHasher(
time_cost=3, # Iterations
memory_cost=65536, # 64 MB
parallelism=1,
hash_len=32,
salt_len=16
)
hashed = ph.hash(password)
is_valid = ph.verify(hashed, password)
`
<code>javascript
<p>// Node.js: bcrypt (widely used)</p>
<p>const bcrypt = require('bcrypt');</p>
<p>const saltRounds = 12;</p>
<p>const hash = await bcrypt.hash(password, saltRounds);</p>
<p>const valid = await bcrypt.compare(password, hash);</p>
</code>
Using a password manager
A password manager solves the human password problem completely. It:
- Generates cryptographically random passwords for every site
- Remembers them all
- Auto-fills on login
Options: Bitwarden (open source, free tier), 1Password, Dashlane. KeePass for self-hosted.
With a password manager, you only need one strong master password. Use a 20-character random password (from a generator) or a 7-word passphrase — and write it on paper kept somewhere secure as backup.
Generating random passwords in code
<code>javascript
<p>// Browser: cryptographically secure</p>
<p>function generatePassword(length = 16, charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*') {</p>
<p>const values = new Uint32Array(length);</p>
<p>crypto.getRandomValues(values);</p>
<p>return Array.from(values, v => charset[v % charset.length]).join('');</p>
<p>}</p>
</code>
`python
import secrets, string
def generate_password(length=16, charset=string.ascii_letters + string.digits + '!@#$%^&*'):
return ''.join(secrets.choice(charset) for _ in range(length))
`
The secrets module (Python) and crypto.getRandomValues() (browser) both use OS-level cryptographic randomness — far better than random or Math.random()`.
Strong passwords are random and long. Everything else is secondary. If you're building an authentication system: use Argon2id for hashing, require at least 8 characters, accept up to 64, and never impose arbitrary complexity rules that lead users toward predictable patterns.
Originally published at https://snappytools.app/password-generator/