Ingeniería de contraseñas
¿De dónde viene la aleatoriedad de las contraseñas?
12 de mayo de 2026 · 6 min de lectura

Los generadores de contraseñas parecen triviales: un clic y un secreto. Lo importante es qué ocurre antes de mostrarlo. ¿De dónde sale el azar? ¿Cómo se convierte en caracteres o palabras? ¿Qué puede hacer quien quiera sumar su propia aleatoriedad?
Este artículo describe el origen del azar en generadores basados en el navegador, cómo entender la entropía y cómo combinar una interacción opcional (por ejemplo un minijuego con teclado) con la fuente criptográfica del navegador.
La respuesta breve
En passwords.lu, la generación ocurre en el navegador. La página pide bytes aleatorios criptográficamente fuertes, los convierte en caracteres o palabras y muestra el resultado de forma local.
Así se obtienen dos propiedades distintas: el azar dificulta la adivinación y la generación local evita enviar el secreto por la infraestructura de la aplicación.
Por qué la generación local cambia el modelo
Muchos sitios antiguos generaban el secreto en el servidor y luego lo mostraban. Ese modelo puede ser honesto, pero impone un problema de confianza evitable.
El usuario debe confiar en que el servidor no registró la contraseña, la cacheó, la expuso en analíticas o la filtró por la infra. La generación nativa en el navegador invierte el papel: el servidor entrega el código y el secreto nace en el dispositivo.
Para un generador de contraseñas, es el modelo de privacidad más limpio: cuantos menos sistemas vean el secreto, menos superficie para errores.
Por qué importa el azar
Las personas somos malos generadores de azar: fechas, nombres, recorridos de teclado, palabras favoritas, formas memorables, pequeñas variantes de lo conocido.
Los atacantes lo saben. Las herramientas no prueban todas las cadenas de aaa a zzz: filtraciones, diccionarios, sustituciones, patrones de teclado, años, combinaciones típicas.
Un generador seguro no pide creatividad: debe tomar valores impredecibles de una fuente aleatoria criptográficamente segura y mapearlos a caracteres o palabras sin sesgo evitable.
Qué significa la entropía aquí
En generación de contraseñas, la entropía mide cuántas posibilidades debe considerar un atacante; suele expresarse en bits.
Un bit: dos casos equiprobables; dos bits: cuatro; diez bits: 1.024. Cada bit adicional duplica el espacio de búsqueda, más rápido de lo que parece.
Si la contraseña se elige al azar y de forma uniforme en un conjunto finito, la entropía es log2(número de posibilidades). Un carácter entre 64 símbolos aporta 6 bits (2^6 = 64).
Por eso importa el método: la fortaleza no viene de que parezca complicada, sino de un conjunto grande y una elección impredecible para el atacante.
Una contraseña de 16 caracteres tomada al azar entre 64 símbolos ronda los 96 bits. Una frase de contraseña al estilo Diceware con 5 palabras de una lista de 7.776 ronda los 64,6 bits (cada palabra ≈ log2(7.776) ≈ 12,9 bits).
Una frase puede ser excelente para memorizar, pero verse más larga no la hace automáticamente más fuerte que una cadena corta bien aleatoria. Cuentan las elecciones aleatorias reales detrás.
La tabla lleva la misma idea a ejemplos concretos de contraseñas y frases de contraseña. Más que el número exacto, importa el patrón: generación uniforme y suficientes elecciones independientes crean el margen de seguridad.
| Ejemplo | Espacio de búsqueda | Entropía | Interpretación |
|---|---|---|---|
| 12 caracteres aleatorios entre 64 símbolos | 64^12 | 72 bits | Fuerte para muchos casos de contraseñas generadas |
| 16 caracteres aleatorios entre 64 símbolos | 64^16 | 96 bits | Muy buen valor por defecto para un gestor |
| 5 palabras Diceware de 7.776 palabras | 7.776^5 | 64,6 bits | Buena base para frase memorizada |
| 6 palabras Diceware de 7.776 palabras | 7.776^6 | 77,5 bits | Frase memorizada más robusta |
Cómo el navegador expone aleatoriedad segura
Los navegadores modernos exponen aleatoriedad criptográfica mediante la Web Crypto API. La función relevante es crypto.getRandomValues(): se entrega un array tipado, por ejemplo un Uint8Array, y el navegador lo rellena con valores fuertes.
La Web Crypto API no llegó a todos a la vez, pero el azar criptográfico en el navegador lleva años siendo amplio. MDN marca la interfaz Crypto y crypto.getRandomValues() como línea base, disponible en los principales navegadores desde julio de 2015.
Nota para desarrolladores: getRandomValues() está pensada para buffers pequeños; la especificación limita cada llamada a 65.536 bytes, así que hay que pedir en trozos si se necesita más.
En forma simplificada, se ve así:
const bytes = new Uint8Array(32);
crypto.getRandomValues(bytes);Esos bytes son materia prima, aún no una contraseña. El siguiente paso es convertirlos en caracteres, palabras de frase, dígitos de PIN o bytes de clave API.
Esa conversión debe hacerse con cuidado. La siguiente sección explica un error frecuente, el sesgo módulo, y cómo lo evitan los generadores seguros.
Qué hace passwords.lu en local
El generador pide bytes seguros al navegador, comprueba que haya fuente segura y rechaza salidas catastróficas (por ejemplo un buffer todo ceros o bytes idénticos). No sustituye al RNG del navegador: es una comprobación simple frente a fallos evidentes.
Para contraseñas con caracteres, los bytes se proyectan al alfabeto elegido con muestreo por rechazo. Para frases, a listas fijas de palabras. Para PIN y similares, el mismo esquema: tirar, rechazar sesgo y luego mapear.
Lo esencial: la contraseña generada sigue siendo un resultado local de la página en tu navegador.
Sesgo módulo: el pequeño error que importa
Error habitual: tomar un byte aleatorio y usar byte % 62 para un alfabeto de 62 símbolos.
Pero 256 no divide en partes iguales a 62: ocho posiciones reciben un valor extra; eso es sesgo módulo.
En un script de juguete parece pequeño; en una herramienta de seguridad cada símbolo permitido debe ser tan uniforme como permita la fuente; el sesgo es innecesario y fácil de evitar.
Muestreo por rechazo: calcular el mayor rango [0, máx) divisible por el tamaño del alfabeto. Con 62 símbolos, aceptar 0–247 (248 es divisible por 62); descartar 248–255 y volver a tirar; entonces byte % 62 queda sin sesgo.
Por eso un generador seguro puede consumir más bytes aleatorios que la longitud final: algunos se descartan a propósito.
function randomIndex(byte, alphabetSize) {
const max = Math.floor(256 / alphabetSize) * alphabetSize;
if (byte >= max) return null; // reject and draw again
return byte % alphabetSize;
}El generador de este repositorio sigue este patrón al mapear bytes a caracteres; la misma idea aplica a índices de palabras y rangos numéricos: tirar bastante azar, rechazar valores que crean cubetas desiguales y solo entonces proyectar.
¿Puede la entrada del usuario añadir aleatoriedad?
Sí: se puede mezclar el azar del navegador con entropía aportada por el usuario. No suele ser necesario si el navegador y el SO están sanos, pero ayuda a la transparencia y a la defensa en profundidad si no se confía plenamente en la fuente local.
Un ejemplo recordado es «remover la olla»: un minijuego o interacción cuyo tiempo y dirección aportan más imprevisibilidad.
Una interacción tipo serpiente recoge cambios de dirección, tiempos entre pulsaciones e historial de movimiento; no es la única fuente, se mezcla con bytes de crypto.getRandomValues().
Trátelo como defensa en profundidad, no como permiso para acortar la contraseña. Mover una serpiente un minuto no hace segura una contraseña de 6 caracteres. Sigue importando la longitud, un espacio de búsqueda grande y una generación sin sesgo.
Un ejemplo concreto es passwordgenerator.ws, que usa una interacción tipo serpiente para mezclar más entrada del usuario.
Cómo mezclar dos fuentes correctamente
No basta con concatenar y esperar. Una sola llamada hash tampoco basta si necesitas un flujo largo. Mejor usar una función de derivación de claves (KDF) con extracción y expansión, como HKDF.
La extracción no es solo compresión: concentra la entropía disponible a partir de entradas quizá estructuradas o parcialmente conocidas en una clave corta pseudoaleatoria — idea central del artículo HKDF de Krawczyk. La expansión deriva los bytes necesarios ligando un contexto claro, p. ej. password-generator/v1.
En esencia: bytes del navegador más bytes de interacción entran en una KDF con contexto; su salida alimenta la generación.
Si al menos una fuente es fuerte y la KDF es correcta, la salida sigue siendo impredecible. La entrada del usuario añade capa útil, pero no sustituye al generador criptográfico del navegador.
Se relaciona con un principio amplio: combinar fuentes solo ayuda con un combinador sólido. El artículo Many a Mickle Makes a Muckle lo estudia en intercambio de claves híbrido. La generación de contraseñas es más simple, pero la lección es la misma: mezclar solo vale si la construcción es sólida.
browserRandom = crypto.getRandomValues(...)
userRandom = collectKeyboardTimingAndMovement(...)
seed = HKDF-Extract(salt = browserRandom, inputKeyMaterial = userRandom)
bytes = HKDF-Expand(seed, info = "password-generator/v1" || context, length = neededBytes)
password = mapBytesToCharactersWithRejectionSampling(bytes)En producción: usar una KDF estándar, contexto explícito para no confundir propósitos, pedir suficientes bytes derivados y mantener muestreo por rechazo al mapear alfabetos.
¿Hay que desconfiar del navegador?
Para el uso habitual, la Web Crypto API es la base adecuada: el navegador delega en el generador criptográfico del sistema operativo.
Ningún generador en el navegador protege frente a todo compromiso local: malware puede observar la página, alterar JavaScript, leer el portapapeles o registrar pulsaciones.
Por eso la meta realista es en capas: navegador de confianza, generar en local, azar criptográfico y guardar en un gestor de contraseñas.
Evita transmitir el secreto si puedes. Si debes compartirlo, usa un canal seguro como privatenote.ai, integrado en passwords.lu.
Para elegir canal, lee nuestra guía de uso compartido seguro sobre correo, chat, nota segura o gestor.
Conclusión práctica
Un buen generador debe ser «aburrido» donde toca: azar criptográfico, sin sesgo, generación local, supuestos claros.
La entrada del usuario puede ayudar, pero la base sigue siendo: suficiente longitud, alfabeto o lista de palabras grande, mapear sin sesgo y que el secreto venga de una fuente criptográfica, no de la imaginación.
Referencias
Generar una contraseña en local
Prueba el generador de passwords.lu: el resultado se queda en tu navegador y no se envía a nuestro servidor.
Abrir generador de contraseñas