1. Sustitución
  2. Enigma
  3. Shannon
  4. DES y AES
  5. Modos
  6. Hashes
  7. Contraseñas
  8. RSA y DH
  9. ECC
  10. Poscuántica

Capítulo 07 · Contraseñas

Lentas a propósito: sal, bcrypt, scrypt y Argon2

Las contraseñas son cortas, humanas y repetidas, y los servidores sufren brechas. Guardarlas bien es el arte de encarecer cada intento: una sal para que cada cuenta haya que atacarla por separado, y un hash deliberadamente lento y exigente en memoria, para que un intento le cueste a la GPU del atacante tanto como al servidor.

En diciembre de 2009 un atacante se descargó la base de datos de usuarios de RockYou, una empresa de juegos para redes sociales: 32 millones de contraseñas, guardadas en texto plano. La lista se convirtió en el diccionario favorito de los atacantes; su entrada más común era 123456, usada por casi 300.000 personas. En 2012 LinkedIn perdió 6,5 millones de contraseñas resumidas con SHA-1, sin sal (la filtración completa, más tarde, tenía 117 millones); la mayoría se rompieron en días. Las contraseñas son el eslabón más débil de casi todos los sistemas, y la forma en que un servidor las guarda decide si una brecha revela unas pocas o casi todas.

No guardes nunca la contraseña

Un servidor no necesita conocer tu contraseña, solo comprobarla. Así que guarda una función unidireccional de ella, y al iniciar sesión recalcula la función sobre lo que has escrito y compara. Cifrar las contraseñas no basta (quien roba la base de datos suele robar también la clave), y un hash rápido sin más tampoco, como demostró LinkedIn. El primer buen diseño es de 1979, cuando Robert Morris y Ken Thompson describieron el esquema de contraseñas de Unix: la contraseña se usaba como clave DES para cifrar un bloque de ceros, repetido 25 veces para hacerlo más lento, con una sal aleatoria de 12 bits que cambiaba el cifrado para cada usuario.

La sal

Sin sal, la misma contraseña tiene siempre el mismo hash. Un atacante puede entonces resumir un diccionario una sola vez y buscar en él todos los hashes robados, y ve de un vistazo qué usuarios comparten contraseña. Martin Hellman mostró en 1980 que el precálculo permite intercambiar tiempo por memoria: con unos N2/3 de memoria se puede invertir una función sobre N valores en un tiempo de unos N2/3 por objetivo. Las tablas arcoíris de Philippe Oechslin (2003) hicieron práctico ese compromiso y rompían los hashes de contraseñas de Windows en segundos.

Proposición (la sal derrota al precálculo)

Si cada contraseña se resume junto con una sal aleatoria independiente de s bits, una tabla precalculada sirve como mucho para un valor de la sal, y un atacante con U usuarios que atacar debe gastar todo el esfuerzo de adivinación en cada uno por separado: el trabajo total se multiplica por min⁡(U,2s) respecto a una base de datos sin sal.

Las sales no son secretas; se guardan junto al hash. Los formatos modernos lo escriben todo en una cadena. La aplicación de escritorio produce, para Argon2id, cadenas como $argon2id$v=19$m=19456,t=2,p=1$sal$hash: algoritmo, versión, coste, sal y hash, así que la verificación no necesita nada más.

¿Cuántos intentos vale una contraseña?

Si una contraseña de longitud L se elige uniformemente al azar en un alfabeto de N símbolos, un atacante necesita de media la mitad de NL intentos, y la contraseña tiene

H=Llog2⁡N bits

de entropía. Ocho minúsculas aleatorias dan 8log2⁡26≈37,6 bits; doce caracteres aleatorios de los 94 símbolos ASCII imprimibles, 78,7 bits. Pero las personas no eligen uniformemente: eligen palabras, nombres, fechas y patrones, con una mayúscula al principio y una cifra al final. Las herramientas de los atacantes (Hashcat, John the Ripper) empiezan por listas filtradas como la de RockYou y les aplican reglas de transformación, y las contraseñas reales caen mucho antes de lo que sugiere su longitud. El cómic de Randall Munroe de 2011 «correct horse battery staple» lo dejó claro: cuatro palabras comunes al azar (unos 44 bits) ganan a una contraseña corta con sustituciones ingeniosas.

Una cota superior de la fuerza de una contraseña, suponiendo que sus caracteres se eligieron al azar, y el tiempo medio que necesitaría una GPU de gama alta para encontrarla según cómo la guardara el servidor. Las velocidades son referencias públicas aproximadas, para mostrar órdenes de magnitud: la forma de guardarla mueve la respuesta ocho órdenes de magnitud, de segundos a siglos.

Las directrices del NIST (SP 800-63B, 2017) cambiaron en consecuencia el consejo oficial: frases largas en lugar de reglas de composición, nada de cambios periódicos obligatorios y comprobar las contraseñas nuevas contra listas de contraseñas filtradas.

Lentas a propósito: PBKDF2

Un hash rápido es una virtud para los ficheros y un defecto para las contraseñas: una GPU calcula miles de millones de SHA-256 por segundo. La solución es el estiramiento de clave: repetir la función muchas veces, para que un intento le cueste al atacante lo mismo que un inicio de sesión al servidor, que puede permitirse unos cientos de milisegundos. PBKDF2 (PKCS #5 v2.0 de RSA Laboratories, 2000; RFC 8018) repite HMAC c veces:

U1=HMACP(S‖INT(i)),Uj=HMACP(Uj−1),Ti=U1⊕U2⊕…⊕Uc.

OWASP recomendaba en 2023 600.000 iteraciones de HMAC-SHA-256. PBKDF2 protege las contraseñas wifi (WPA2, con 4.096 iteraciones), los gestores de contraseñas y el cifrado de disco completo, y está aprobado por FIPS.

PBKDF2-HMAC-SHA256 real, calculado por el WebCrypto de tu navegador. Elige el número de iteraciones y deriva una clave: lo que tarda aquí es el coste de un intento en este dispositivo. Una GPU es mucho más rápida, pero cada iteración multiplica su trabajo igualmente.

bcrypt

En 1999 Niels Provos y David Mazières, para OpenBSD, construyeron bcrypt sobre la costosa preparación de clave de Blowfish (capítulo 04), repetida 2coste veces, de modo que el coste crece exponencialmente con un único parámetro que se puede subir a medida que mejora el hardware. bcrypt usa algo de memoria (4 KB de S-boxes que cambian sin parar), lo que ya perjudica a las GPU, y veinticinco años después sigue siendo una buena opción. Su única trampa: solo usa los primeros 72 bytes de la contraseña; la aplicación de escritorio rechaza las más largas en vez de truncarlas en silencio.

Funciones exigentes en memoria: scrypt

Los atacantes no usan CPU: usan GPU y, para los objetivos más valiosos, chips a medida, que tienen miles de núcleos pero poca memoria por núcleo. scrypt, de Colin Percival (2009, para su servicio de copias de seguridad Tarsnap), llena un búfer grande con datos pseudoaleatorios y luego lo vuelve a leer en un orden que depende de los datos, de modo que no se puede calcular deprisa sin tener todo el búfer en memoria.

Idea (exigencia de memoria)

El coste del hardware de un atacante es, a grandes rasgos, el área del chip por el tiempo que se usa. Una función que necesita memoria M durante un tiempo T, y que no se puede calcular con mucha menos memoria sin frenarse mucho, cuesta unos M×T por intento. Subir M encarece cada núcleo paralelo del atacante, no solo lo frena.

Con los parámetros N=217, r=8, scrypt necesita 128 MiB por intento. Litecoin lo adoptó como prueba de trabajo en 2011, lo que enseguida produjo ASIC de scrypt: la exigencia de memoria sube el coste, pero no hace imposible el hardware dedicado.

Argon2: el ganador del concurso

Como con AES y SHA-3, la comunidad organizó un concurso abierto: la Password Hashing Competition (2013–2015) recibió 24 candidatos y eligió Argon2, de Alex Biryukov, Daniel Dinu y Dmitry Khovratovich, de la Universidad de Luxemburgo. Tiene tres parámetros: la memoria (en KiB), las pasadas sobre la memoria y los carriles en paralelo. Su variante híbrida Argon2id empieza con accesos a memoria independientes de los datos (que resisten los ataques de canal lateral) y sigue con accesos dependientes de los datos (que resisten los compromisos tiempo–memoria); se estandarizó en la RFC 9106 (2021). El mínimo de OWASP es 19 MiB, dos pasadas y un carril, lo que usa por defecto la aplicación de escritorio; sube la memoria si tu servidor puede permitírselo.

Cifrar con una contraseña

Las mismas funciones convierten una contraseña en una clave de cifrado. «AES-GCM + Argon2id», en la aplicación de escritorio, lo hace a la manera moderna: una sal aleatoria de 16 bytes, Argon2id para derivar una clave de 256 bits y AES-GCM con un nonce aleatorio, de modo que una contraseña equivocada o un texto cifrado modificado se detectan. La salida lleva todo lo necesario salvo la contraseña: un byte de versión, la sal, el nonce y el texto cifrado con su etiqueta.

cryptoKit 1.0 y Jasypt

La primera versión de cryptoKit (2022) ofrecía cifrado con contraseña mediante la biblioteca Jasypt: PBEWithHMACSHA512AndAES_256, que es PBKDF2 con HMAC-SHA-512, 1.000 iteraciones, una sal de 16 bytes y AES-256 en modo CBC. En 2022 ese número de iteraciones ya estaba seiscientas veces por debajo de lo recomendado, y el esquema no autentica: una contraseña equivocada suele notarse solo como un error de relleno. La versión 2 lo mantiene, reimplementado solo con el JDK y probado en los dos sentidos contra Jasypt, para que los textos cifrados antiguos se puedan seguir descifrando; para datos nuevos, usa AES-GCM + Argon2id.

Para saber más

  1. Robert Morris y Ken Thompson, «Password Security: A Case History», Communications of the ACM 22(11), 1979.
  2. Philippe Oechslin, «Making a Faster Cryptanalytic Time-Memory Trade-Off», CRYPTO 2003.
  3. Niels Provos y David Mazières, «A Future-Adaptable Password Scheme», USENIX 1999.
  4. Colin Percival, «Stronger Key Derivation via Sequential Memory-Hard Functions», BSDCan 2009.
  5. Alex Biryukov, Daniel Dinu, Dmitry Khovratovich et al., RFC 9106, Argon2 Memory-Hard Function (2021); OWASP Password Storage Cheat Sheet.