Seguridad de contraseñas en 2026: longitud, entropía y mejores prácticas

10 min15 de mayo de 2026

El estado de la seguridad de contraseñas en 2026

Las contraseñas siguen siendo el mecanismo de autenticación más extendido en 2026, a pesar de décadas de intentos por reemplazarlas. Passkeys y WebAuthn están ganando adopción, pero la realidad es que la mayoría de sistemas todavía dependen de contraseñas como factor primario o de respaldo. Entender cómo funcionan los ataques y cómo se mide la fortaleza de una contraseña sigue siendo conocimiento esencial para cualquier desarrollador.

El panorama de amenazas ha evolucionado significativamente. Las GPUs modernas (como la NVIDIA RTX 5090) pueden probar más de 100 mil millones de hashes MD5 por segundo. Los ataques de diccionario usan datasets de miles de millones de contraseñas filtradas en brechas anteriores. Y los ataques basados en reglas (combinando palabras del diccionario con sustituciones comunes como @ por "a" o 3 por "e") son extremadamente efectivos contra contraseñas que parecen complejas pero siguen patrones predecibles.

La buena noticia: con las prácticas correctas del lado del servidor (funciones de hashing lentas) y del lado del usuario (contraseñas largas y aleatorias), es perfectamente posible crear contraseñas que resistirían siglos de ataque. El problema es la brecha entre lo teóricamente seguro y lo que la gente realmente hace.

Entropía: cómo medir la fortaleza real

La entropía mide la imprevisibilidad de una contraseña en bits. Una contraseña con n bits de entropía requiere, en promedio, 2^(n-1) intentos para adivinarla por fuerza bruta. La fórmula es simple: entropía = log2(posibilidades^longitud) = longitud × log2(tamaño_del_alfabeto). Para una contraseña de 12 caracteres usando minúsculas, mayúsculas, dígitos y 10 símbolos (72 caracteres posibles): 12 × log2(72) ≈ 74 bits.

Pero esta fórmula solo aplica a contraseñas verdaderamente aleatorias. Si eliges "MiGato2026!" pensando que es segura, su entropía real es mucho menor que los 75+ bits que sugiere el cálculo por caracteres. Los atacantes no prueban combinaciones al azar — usan diccionarios, patrones comunes (palabra + año + símbolo) y reglas de transformación. La entropía efectiva de "MiGato2026!" está más cerca de 30-40 bits.

El estándar actual del NIST (SP 800-63B, revisado en 2024) recomienda un mínimo de 8 caracteres sin requisitos de complejidad artificial. Sin embargo, para cuentas de alto valor (administradores, acceso a infraestructura), se recomiendan al menos 128 bits de entropía — lo que equivale a 20 caracteres aleatorios de un alfabeto de 72 caracteres, o una passphrase de 10 palabras aleatorias.

La conclusión práctica: la longitud importa más que la complejidad. Una contraseña de 20 caracteres solo con minúsculas (20 × log2(26) ≈ 94 bits) es más segura que una de 8 caracteres con todos los tipos de caracteres (8 × log2(72) ≈ 49 bits). Y es más fácil de recordar si usas palabras aleatorias.

// Calcular entropía de una contraseña aleatoria
function calcularEntropia(longitud, tamanoAlfabeto) {
  return longitud * Math.log2(tamanoAlfabeto);
}

// Ejemplos de entropía:
calcularEntropia(8, 26);   // 37.6 bits — solo minúsculas, 8 chars
calcularEntropia(8, 72);   // 49.4 bits — todos los tipos, 8 chars
calcularEntropia(12, 72);  // 74.0 bits — todos los tipos, 12 chars
calcularEntropia(16, 72);  // 98.7 bits — todos los tipos, 16 chars
calcularEntropia(20, 26);  // 94.0 bits — solo minúsculas, 20 chars

// Passphrase: 4 palabras de un diccionario de 7776 (tipo Diceware)
calcularEntropia(4, 7776); // 51.7 bits
calcularEntropia(6, 7776); // 77.5 bits
calcularEntropia(8, 7776); // 103.4 bits

// Tiempo estimado para romper por fuerza bruta
// (asumiendo 10 mil millones de intentos/segundo con bcrypt + GPU cluster)
function tiempoParaRomper(bits) {
  const intentos = 2 ** (bits - 1); // promedio
  const porSegundo = 10_000; // bcrypt es lento: ~10k/s en GPU cluster
  const segundos = intentos / porSegundo;
  const anios = segundos / (365.25 * 24 * 3600);
  return anios;
}

Por qué la longitud supera a la complejidad

Durante décadas, las políticas de contraseñas exigían requisitos como "al menos una mayúscula, un número y un símbolo especial". Investigaciones recientes (y la experiencia práctica) demuestran que esto es contraproducente. Los usuarios responden de forma predecible: "Password1!", "Verano2026#", "MiEmpresa1!". Estos patrones son los primeros que prueban los atacantes.

Cada carácter adicional multiplica el espacio de búsqueda exponencialmente. Pasar de 8 a 12 caracteres con el mismo alfabeto de 72 símbolos incrementa las combinaciones de 7×10^14 a 1.9×10^22 — un factor de 27 millones. Pero añadir símbolos al alfabeto manteniendo la longitud tiene un efecto mucho menor: pasar de 62 caracteres (sin símbolos) a 72 con 8 de longitud solo multiplica por 3.5x.

El enfoque moderno preferido es el uso de passphrases: secuencias de palabras aleatorias separadas por espacios o guiones. "caballo correcto batería grapa" tiene ~51 bits de entropía si las palabras se eligen aleatoriamente de un diccionario grande, y es incomparablemente más fácil de recordar que "Xk9#mP2$". Con 6-8 palabras aleatorias alcanzas 77-103 bits — más que suficiente para la mayoría de propósitos.

El NIST actualizó sus directrices para reflejar esto: se desaconsejan los requisitos de complejidad, se fomenta la longitud (hasta 64+ caracteres), se prohíbe truncar contraseñas, y se recomienda comprobar contra listas de contraseñas filtradas en lugar de imponer reglas de composición arbitrarias. Si estás implementando un sistema de autenticación, sigue estas directrices.

Funciones de hashing para contraseñas

Nunca almacenes contraseñas en texto plano ni con hashes rápidos como MD5 o SHA-256. Las funciones de hashing para contraseñas están diseñadas para ser deliberadamente lentas, consumiendo tiempo y memoria para hacer los ataques de fuerza bruta computacionalmente inviables. Las tres opciones recomendadas en 2026 son: Argon2id (ganador del Password Hashing Competition), bcrypt (maduro y bien probado), y scrypt.

Argon2id es la recomendación principal actual. Permite configurar tres parámetros: tiempo (iteraciones), memoria (en KB) y paralelismo (hilos). Una configuración razonable para un servidor web es: 3 iteraciones, 64 MB de memoria, 1 hilo de paralelismo. Esto produce un hash en ~200ms en hardware de servidor típico — imperceptible para el usuario pero devastador para un atacante que necesita millones de intentos.

bcrypt sigue siendo perfectamente válido si ya lo usas. Su factor de coste (work factor) controla la lentitud: un factor de 12 toma ~250ms en hardware moderno. La limitación principal de bcrypt es que trunca contraseñas a 72 bytes — si permites contraseñas más largas (como deberías), pre-hashea con SHA-256 antes de bcrypt, o migra a Argon2id.

Cada hash debe incluir un salt aleatorio único. Un salt es un valor aleatorio (16+ bytes) que se concatena con la contraseña antes de hashear. Esto significa que dos usuarios con la misma contraseña tendrán hashes diferentes, y un atacante no puede usar tablas rainbow precalculadas. Tanto bcrypt como Argon2id incluyen el salt automáticamente en su salida — no necesitas gestionarlo manualmente.

// Node.js: hashing con Argon2id (recomendado)
import argon2 from 'argon2';

// Hashear una contraseña
const hash = await argon2.hash(password, {
  type: argon2.argon2id,
  memoryCost: 65536,  // 64 MB
  timeCost: 3,        // 3 iteraciones
  parallelism: 1      // 1 hilo
});
// "$argon2id$v=19$m=65536,t=3,p=1$salt$hash..."

// Verificar una contraseña
const valido = await argon2.verify(hash, passwordIngresado);

// Alternativa: bcrypt (también válido)
import bcrypt from 'bcrypt';
const hashBcrypt = await bcrypt.hash(password, 12); // factor 12
const validoBcrypt = await bcrypt.compare(passwordIngresado, hashBcrypt);

Ataques comunes y sus contramedidas

Fuerza bruta: probar todas las combinaciones posibles. Contramedida: contraseñas con 80+ bits de entropía y funciones de hashing lentas. Con Argon2id configurado para 200ms por intento, un atacante con 1000 GPUs necesitaría un tiempo astronómico para 80 bits de entropía.

Ataques de diccionario: probar palabras comunes y variaciones. Contramedida: usar contraseñas verdaderamente aleatorias (no basadas en palabras predecibles). Si usas passphrases, elige las palabras con un generador aleatorio, no de tu cabeza — los humanos somos terribles eligiendo al "azar". También implementa comprobación contra listas de contraseñas filtradas (como la base de datos de Have I Been Pwned).

Credential stuffing: usar combinaciones usuario/contraseña filtradas de otros servicios. Contramedida: autenticación multifactor (MFA). No importa cuán fuerte sea la contraseña si el usuario la reutiliza en 20 sitios y uno de ellos sufre una brecha. MFA (TOTP, WebAuthn, SMS como último recurso) neutraliza este ataque completamente.

Phishing: engañar al usuario para que introduzca su contraseña en un sitio falso. Contramedida: WebAuthn/passkeys son resistentes a phishing por diseño (vinculan la credencial al dominio). Para contraseñas tradicionales, la educación del usuario y los gestores de contraseñas (que no auto-rellenan en dominios incorrectos) son la mejor defensa.

Gestores de contraseñas: la solución práctica

El consejo de usar contraseñas únicas, largas y aleatorias para cada servicio es inviable sin un gestor de contraseñas. Un humano no puede memorizar 100+ contraseñas de 20 caracteres aleatorios. Los gestores de contraseñas resuelven este problema: generas una contraseña maestra fuerte (passphrase de 5-6 palabras) y el gestor almacena y autorellena el resto.

Las opciones actuales incluyen 1Password, Bitwarden (open source), KeePassXC (local, sin cloud), y los gestores integrados en navegadores (Chrome, Firefox, Safari). Para desarrolladores, Bitwarden CLI es especialmente útil para integrar secretos en scripts y pipelines CI/CD sin exponerlos en código fuente.

Implementa soporte para gestores de contraseñas en tus aplicaciones: permite paste en campos de contraseña (nunca bloquees Ctrl+V), no uses autocompletar deshabilitado innecesariamente, usa los atributos autocomplete correctos (autocomplete="current-password" o "new-password"), y no impongas longitudes máximas ridículas. He visto bancos que limitan contraseñas a 12 caracteres — esto rompe gestores de contraseñas y fuerza contraseñas débiles.

Para la contraseña maestra del propio gestor, usa una passphrase de al menos 5 palabras Diceware generadas con dados físicos o un generador criptográficamente seguro. Esta es la única contraseña que necesitas memorizar. Si la olvidas, perderás acceso a todas las demás — así que anótala en papel y guárdala en un lugar seguro (caja fuerte, no un post-it en el monitor).

Implementación segura para desarrolladores

Al implementar un sistema de autenticación, sigue estas prácticas: mínimo 8 caracteres sin máximo artificial (acepta hasta al menos 128), sin requisitos de complejidad forzados, comprobación contra la lista de contraseñas comprometidas de Have I Been Pwned (API k-anonymity para no exponer la contraseña), y medidor visual de fortaleza basado en entropía real (librería zxcvbn o similar).

Rate limiting es esencial: limita intentos de login a 5-10 por minuto por cuenta, con bloqueo temporal exponencial. Implementa CAPTCHA después de 3 intentos fallidos. No reveles si el email existe o no en mensajes de error ("credenciales inválidas", nunca "usuario no encontrado"). Registra todos los intentos fallidos para detección de anomalías.

Para el reseteo de contraseña: genera tokens criptográficamente aleatorios de al menos 32 bytes, con expiración de 1 hora máximo y uso único. Envía el link por email (nunca muestres la nueva contraseña en pantalla). Invalida todas las sesiones activas cuando se cambia la contraseña. Y no uses preguntas de seguridad — son triviales de investigar en redes sociales.

Considera implementar detección de credential stuffing: si un usuario intenta iniciar sesión con una contraseña que aparece en brechas conocidas, muestra una advertencia y recomienda cambiarla. Puedes verificar esto usando la API de Have I Been Pwned con k-anonymity (envías solo los primeros 5 caracteres del hash SHA-1 de la contraseña, sin exponer la contraseña completa).

// Verificar si una contraseña está comprometida (Have I Been Pwned)
import crypto from 'crypto';

async function estaComprometida(password: string): Promise<boolean> {
  const sha1 = crypto.createHash('sha1')
    .update(password).digest('hex').toUpperCase();
  const prefijo = sha1.slice(0, 5);
  const sufijo = sha1.slice(5);

  const response = await fetch(
    `https://api.pwnedpasswords.com/range/${prefijo}`
  );
  const texto = await response.text();

  // Buscar nuestro sufijo en la respuesta
  return texto.split('\n').some(linea =>
    linea.startsWith(sufijo)
  );
}

// Medidor de fortaleza con zxcvbn
import zxcvbn from 'zxcvbn';
const resultado = zxcvbn("MiContraseña123");
console.log(resultado.score);        // 0-4 (0=muy débil, 4=muy fuerte)
console.log(resultado.feedback);     // Sugerencias de mejora
console.log(resultado.crack_times_display); // Tiempos estimados de crackeo

El futuro: passkeys y autenticación sin contraseña

WebAuthn y passkeys representan el futuro de la autenticación. En lugar de un secreto compartido (la contraseña, que existe tanto en tu cabeza como en el servidor), passkeys usan criptografía asimétrica: una clave privada almacenada de forma segura en tu dispositivo y una clave pública en el servidor. Incluso si el servidor sufre una brecha, las claves públicas son inútiles para un atacante.

Las passkeys son resistentes a phishing por diseño — el navegador verifica que el dominio coincide con el registrado en la credencial. No se pueden reutilizar entre servicios. No se pueden adivinar por fuerza bruta. Y la experiencia de usuario es superior: desbloqueo biométrico o PIN del dispositivo en lugar de escribir una contraseña larga.

Sin embargo, la transición será gradual. Muchos servicios todavía no soportan passkeys. Los usuarios necesitan métodos de recuperación (que a menudo vuelven a ser contraseñas o códigos por email). Y la sincronización de passkeys entre dispositivos depende de ecosistemas (iCloud Keychain, Google Password Manager, 1Password). Por ahora, la contraseña fuerte sigue siendo necesaria como respaldo.

Como desarrollador, implementa soporte para passkeys (la API WebAuthn está bien documentada y las librerías como SimpleWebAuthn simplifican la integración). Pero mantén la opción de contraseña como alternativa y aplica todas las prácticas de seguridad descritas en esta guía. El objetivo es ofrecer passkeys como opción preferida mientras mantienes contraseñas seguras como fallback.