Generar contraseñas seguras no es tan trivial como parece. Math.random() no es criptográficamente seguro. La solución correcta está en la Web Crypto API, disponible en todos los navegadores modernos.
El problema con Math.random()
// MAL — no usar para contraseñas
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const password = Array.from({ length: 16 }, () =>
chars[Math.floor(Math.random() * chars.length)]
).join('');
Math.random() usa un generador pseudoaleatorio predecible. No sirve para seguridad.
La solución correcta: crypto.getRandomValues()
function generatePassword(length = 16, options = {}) {
const {
uppercase = true,
lowercase = true,
numbers = true,
symbols = false,
} = options;
let charset = '';
if (uppercase) charset += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
if (lowercase) charset += 'abcdefghijklmnopqrstuvwxyz';
if (numbers) charset += '0123456789';
if (symbols) charset += '!@#$%^&*()_+-=[]{}|;:,.<>?';
if (!charset) throw new Error('Al menos un tipo de caracter requerido');
const array = new Uint32Array(length);
crypto.getRandomValues(array);
return Array.from(array, (n) => charset[n % charset.length]).join('');
}
Por qué funciona
crypto.getRandomValues() usa la fuente de entropía del sistema operativo (equivalente a /dev/urandom en Linux). Los valores son impredecibles incluso conociendo los anteriores.
El truco del módulo (n % charset.length) introduce un pequeño sesgo si el tamaño del charset no divide exactamente a 2^32. Para longitudes de charset típicas (62-95 caracteres) el sesgo es despreciable, pero si quieres eliminarlo del todo:
function unbiasedRandom(max, array, index) {
const limit = Math.floor(0xFFFFFFFF / max) * max;
if (array[index] > limit) return null; // descartar
return array[index] % max;
}
Calcular la entropía
function entropy(length, charsetSize) {
return length * Math.log2(charsetSize);
}
entropy(16, 62); // 95.3 bits — muy seguro
entropy(12, 62); // 71.5 bits — aceptable
entropy(8, 62); // 47.6 bits — débil
A partir de 80 bits de entropía una contraseña es prácticamente infuerectable con hardware actual.
Comprobar la fortaleza
function strength(password) {
let charset = 0;
if (/[a-z]/.test(password)) charset += 26;
if (/[A-Z]/.test(password)) charset += 26;
if (/[0-9]/.test(password)) charset += 10;
if (/[^a-zA-Z0-9]/.test(password)) charset += 32;
const bits = password.length * Math.log2(charset);
if (bits < 50) return 'débil';
if (bits < 80) return 'media';
return 'fuerte';
}
Herramienta lista para usar
Si prefieres no implementarlo y solo necesitas generar contraseñas seguras rápidamente, puedes usar este generador de contraseñas gratuito que funciona íntegramente en el navegador, sin enviar nada a ningún servidor.
Conclusión
- Usa siempre
crypto.getRandomValues(), nuncaMath.random() - 16 caracteres con mayúsculas, minúsculas y números dan más de 95 bits de entropía
- La Web Crypto API está disponible en todos los navegadores modernos sin instalar nada
Top comments (0)