Password Generator — How to Build a Secure Random Password Generator
Build a cryptographically secure password generator in JavaScript and Python. Learn character set composition, entropy calculation, browser crypto.getRandomValues, and how to...
A secure password generator must use a cryptographically random source — never Math.random(). Here’s how to build one in JavaScript and Python with proper entropy.
Use the Password Generator to generate secure passwords instantly.
Why not Math.random()
// WRONG — Math.random() is not cryptographically secure:
function badPassword(length) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
return Array.from({ length }, () => chars[Math.floor(Math.random() * chars.length)]).join('');
}
// Math.random() is a pseudo-random number generator (PRNG)
// Its output can be predicted with enough samples
Cryptographically secure in the browser
function generatePassword(length = 16, options = {}) {
const {
uppercase = true,
lowercase = true,
digits = true,
symbols = true,
excludeAmbiguous = false, // l, 1, I, O, 0
} = options;
let chars = '';
if (uppercase) chars += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
if (lowercase) chars += 'abcdefghijklmnopqrstuvwxyz';
if (digits) chars += '0123456789';
if (symbols) chars += '!@#$%^&*()_+-=[]{}|;:,.<>?';
if (excludeAmbiguous) {
chars = chars.replace(/[lI1O0]/g, '');
}
if (!chars) throw new Error('At least one character type required');
const array = new Uint32Array(length);
crypto.getRandomValues(array); // CSPRNG
return Array.from(array, n => chars[n % chars.length]).join('');
}
// Usage:
generatePassword(20)
// 'k4$Hm9pL#rN2xQjW7@Yv'
generatePassword(16, { uppercase: true, digits: true, symbols: false })
// 'kHmpLrNxQjWvBcDe'
Avoid modulo bias
The n % chars.length approach introduces a slight bias — values near 0 are slightly more likely than others. For maximum security, reject values that would cause bias:
function unbiasedPassword(length, chars) {
const array = new Uint8Array(length * 2);
const result = [];
const limit = 256 - (256 % chars.length); // Max value without bias
let i = 0;
while (result.length < length) {
crypto.getRandomValues(array.subarray(i, i + 1));
if (array[i] < limit) {
result.push(chars[array[i] % chars.length]);
}
i++;
if (i >= array.length) {
i = 0; // Refill buffer
}
}
return result.join('');
}
Python: secrets module
import secrets
import string
def generate_password(
length: int = 16,
uppercase: bool = True,
lowercase: bool = True,
digits: bool = True,
symbols: bool = True,
) -> str:
chars = ''
if uppercase: chars += string.ascii_uppercase
if lowercase: chars += string.ascii_lowercase
if digits: chars += string.digits
if symbols: chars += '!@#$%^&*()_+-=[]{}|;:,.<>?'
if not chars:
raise ValueError('At least one character type required')
# secrets.choice uses os.urandom() — cryptographically secure
return ''.join(secrets.choice(chars) for _ in range(length))
# Ensure all required character types are present:
def generate_strong_password(length: int = 16) -> str:
while True:
password = generate_password(length)
if (any(c.isupper() for c in password) and
any(c.islower() for c in password) and
any(c.isdigit() for c in password) and
any(c in '!@#$%^&*' for c in password)):
return password
Entropy calculation
Entropy determines how hard a password is to brute-force:
function calculateEntropy(password) {
const charsets = [
{ test: /[a-z]/, size: 26, name: 'lowercase' },
{ test: /[A-Z]/, size: 26, name: 'uppercase' },
{ test: /[0-9]/, size: 10, name: 'digits' },
{ test: /[^a-zA-Z0-9]/, size: 32, name: 'symbols' },
];
const poolSize = charsets.reduce((size, { test }) =>
test.test(password) ? size + charsets.find(c => c.test === test).size : size
, 0);
// Entropy = log2(poolSize^length)
const entropy = password.length * Math.log2(poolSize);
return {
entropy: Math.round(entropy),
bits: `${Math.round(entropy)} bits`,
strength: entropy < 40 ? 'Weak' : entropy < 60 ? 'Fair' : entropy < 80 ? 'Good' : 'Excellent',
};
}
calculateEntropy('password') // { entropy: 37, strength: 'Weak' }
calculateEntropy('P@ssw0rd!') // { entropy: 52, strength: 'Fair' }
calculateEntropy('k4$Hm9pL#rN') // { entropy: 72, strength: 'Good' }
Pronounceable password (alternative)
function generatePronounceable(length = 16) {
const consonants = 'bcdfghjklmnprstvwxyz';
const vowels = 'aeiou';
const digits = '0123456789';
let result = '';
const arr = new Uint8Array(length * 2);
crypto.getRandomValues(arr);
let i = 0;
while (result.length < length) {
const isConsonant = result.length % 2 === 0;
const set = isConsonant ? consonants : vowels;
result += set[arr[i] % set.length];
i++;
}
// Add a digit and capitalize first letter:
return result.charAt(0).toUpperCase() + result.slice(1) + digits[arr[i] % digits.length];
}
generatePronounceable() // 'Balexomipuratef3'
Related tools
- Password Generator — generate secure passwords online
- Password Strength Checker — entropy and complexity scoring
- Hash Functions — bcrypt and Argon2 for password storage
Related posts
- How Secure Is My Password? Entropy, Crack Times, and What Actually Matters — Password security measured in bits of entropy, real hashcat benchmarks on RTX 40…
- Brute Force Password Attacks — How They Work and How to Defend Against Them — Brute force attacks try every possible password combination. Learn how attackers…
- Diceware Passphrases — Stronger and More Memorable Than Passwords — Diceware generates memorable passphrases by rolling dice to select words from a …
- Hash Functions Comparison — MD5, SHA-1, SHA-256, bcrypt, Argon2 — Hash functions have different speed, output size, and security properties. MD5 a…
- Password Manager Guide — Why You Need One and How to Choose — Password managers store and generate strong unique passwords for every site. Her…
Related tool
Generate strong random passwords with configurable length, character classes, and exclusions. Real entropy meter, crack-time estimate, bulk mode.
Written by Mian Ali Khalid. Part of the Dev Productivity pillar.