DEV Community

ToolRapido
ToolRapido

Posted on

Cómo generar contraseñas seguras en JavaScript con la Web Crypto API

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('');
Enter fullscreen mode Exit fullscreen mode

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('');
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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';
}
Enter fullscreen mode Exit fullscreen mode

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(), nunca Math.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)