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 este capítulo
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 de memoria se puede invertir una función sobre valores en un tiempo de unos 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.
Si cada contraseña se resume junto con una sal aleatoria independiente de bits, una tabla precalculada sirve como mucho para un valor de la sal, y un atacante con usuarios que atacar debe gastar todo el esfuerzo de adivinación en cada uno por separado: el trabajo total se multiplica por 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 se elige uniformemente al azar en un alfabeto de símbolos, un atacante necesita de media la mitad de intentos, y la contraseña tiene
de entropía. Ocho minúsculas aleatorias dan bits; doce caracteres aleatorios de los 94 símbolos ASCII imprimibles, 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.
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 veces:
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.
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 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.
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 durante un tiempo , y que no se puede calcular con mucha menos memoria sin frenarse mucho, cuesta unos por intento. Subir encarece cada núcleo paralelo del atacante, no solo lo frena.
Con los parámetros , , 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
- Robert Morris y Ken Thompson, «Password Security: A Case History», Communications of the ACM 22(11), 1979.
- Philippe Oechslin, «Making a Faster Cryptanalytic Time-Memory Trade-Off», CRYPTO 2003.
- Niels Provos y David Mazières, «A Future-Adaptable Password Scheme», USENIX 1999.
- Colin Percival, «Stronger Key Derivation via Sequential Memory-Hard Functions», BSDCan 2009.
- Alex Biryukov, Daniel Dinu, Dmitry Khovratovich et al., RFC 9106, Argon2 Memory-Hard Function (2021); OWASP Password Storage Cheat Sheet.