Password Strength Checkers: From Character Sets to Entropy Calculation
Password Strength Checkers: From Character Sets to Entropy Calculation#
Ever wondered how those “Password Strength: Strong” indicators work? Why do some sites require uppercase, lowercase, numbers, and special characters? Let’s break down the math behind password strength detection.
The Core: Information Entropy#
Password strength is fundamentally about information entropy - measuring the uncertainty in a password. Higher entropy means harder to guess.
The formula:
Entropy = log₂(N^L) = L × log₂(N)
Where:
- N = character set size (number of possible characters)
- L = password length
For example, an 8-character lowercase password:
- N = 26 (a-z)
- L = 8
- Entropy = 8 × log₂(26) ≈ 37.6 bits
An 8-character mixed password (uppercase + lowercase + numbers + symbols):
- N = 26 + 26 + 10 + 32 = 94
- L = 8
- Entropy = 8 × log₂(94) ≈ 52.4 bits
That 15-bit difference means the harder password takes 2^15 = 32,768 times longer to crack!
Crack Time Estimation#
With entropy, we can estimate brute-force time:
const charset =
(hasLowercase ? 26 : 0) +
(hasUppercase ? 26 : 0) +
(hasNumbers ? 10 : 0) +
(hasSpecial ? 32 : 0)
const combinations = Math.pow(charset || 1, password.length)
const guessesPerSecond = 10_000_000_000 // Modern GPU: ~10 billion/sec
const seconds = combinations / guessesPerSecond
Convert seconds to human-readable time:
function formatCrackTime(seconds: number): string {
if (seconds < 1) return 'Instant'
if (seconds < 60) return `${Math.round(seconds)} seconds`
if (seconds < 3600) return `${Math.round(seconds / 60)} minutes`
if (seconds < 86400) return `${Math.round(seconds / 3600)} hours`
if (seconds < 31536000) return `${Math.round(seconds / 86400)} days`
return `${Math.round(seconds / 31536000)} years`
}
A 12-character mixed password takes about 17.8 billion years to crack - Earth won’t even exist by then.
Scoring Algorithm#
Raw entropy isn’t intuitive, so we design a 6-point scoring system:
function analyzePassword(password: string): StrengthResult {
let score = 0
const checks = {
length: password.length >= 8, // At least 8 chars
uppercase: /[A-Z]/.test(password), // Has uppercase
lowercase: /[a-z]/.test(password), // Has lowercase
numbers: /\d/.test(password), // Has numbers
special: /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password),
minLength: password.length >= 12, // Extra point for 12+ chars
}
if (checks.length) score += 1
if (checks.uppercase) score += 1
if (checks.lowercase) score += 1
if (checks.numbers) score += 1
if (checks.special) score += 1
if (checks.minLength) score += 1
// ... penalty logic follows
}
Common Password Blacklist#
Some passwords pass the rules but are too common. They need penalties:
const COMMON_PASSWORDS = [
'123456', 'password', '12345678', 'qwerty', '123456789',
'letmein', '1234567', 'football', 'iloveyou', 'admin',
'welcome', 'monkey', 'login', 'abc123', '111111',
]
if (COMMON_PASSWORDS.includes(password.toLowerCase())) {
score = Math.max(0, score - 3) // Deduct 3 points
}
123456 and password top the leaked password lists every year. They’re cracked instantly via rainbow tables.
Repeated Character Detection#
aaaaaa has decent entropy on paper but is actually weak. Detect consecutive repeats:
if (/(.)\1{2,}/.test(password)) {
score = Math.max(0, score - 1)
}
The regex (.)\1{2,} means: any character appearing 3+ times consecutively. \1 is a backreference to the first capture group.
Strong Password Generator#
Detection is half the battle. Here’s a generation trick: guarantee each character type:
function generatePassword(): string {
const charset = {
uppercase: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
lowercase: 'abcdefghijklmnopqrstuvwxyz',
numbers: '0123456789',
special: '!@#$%^&*()_+-=[]{}|;:,.<>?',
}
let password = ''
// Guarantee at least one of each type
password += charset.uppercase[Math.floor(Math.random() * 26)]
password += charset.lowercase[Math.floor(Math.random() * 26)]
password += charset.numbers[Math.floor(Math.random() * 10)]
password += charset.special[Math.floor(Math.random() * 32)]
// Fill remaining positions randomly
const allChars = Object.values(charset).join('')
for (let i = password.length; i < 16; i++) {
password += allChars[Math.floor(Math.random() * allChars.length)]
}
// Shuffle the result
return password.split('').sort(() => Math.random() - 0.5).join('')
}
The final shuffle is crucial - otherwise the first 4 characters would always follow the “uppercase + lowercase + number + symbol” pattern.
Security Considerations#
1. Don’t Use Math.random()#
Math.random() is pseudo-random and predictable. Production code should use crypto.getRandomValues():
const array = new Uint32Array(1)
crypto.getRandomValues(array)
const randomIndex = array[0] % charsetLength
2. Frontend Detection Limitations#
Client-side checks only stop honest users. Real security requires:
- Server-side salted hashing (bcrypt/scrypt/Argon2)
- Rate limiting against brute force
- Multi-factor authentication (MFA)
- Breach detection (Have I Been Pwned API)
3. Never Transmit Passwords in URLs or Logs#
Even for strength checking, passwords should only exist in memory. Never send them to a server.
Try It Yourself#
Built on these principles: Password Strength Checker
Features:
- Real-time 6-point scoring
- Crack time estimation
- Improvement suggestions
- One-click strong password generation
- Common password blacklist detection
Next time you set a password, check its entropy first - your data security might depend on those extra characters.
Related: Password Generator | Hash Generator