La evolución del hashing de contraseñas
Es posible que hayas escuchado consejos para elegir algoritmos de hashing de contraseñas, pero ¿has pensado por qué se recomiendan? En este artículo, exploraremos la evolución de los algoritmos de hashing de contraseñas y los motivos detrás de ellos.
Introducción
El hashing de contraseñas, como su nombre indica, es el proceso de calcular un valor hash a partir de una contraseña. El valor hash se almacena normalmente en una base de datos y, durante el proceso de inicio de sesión, se calcula el valor hash de la contraseña introducida por el usuario y se compara con el valor hash almacenado en la base de datos. Si coinciden, se autentica al usuario.
Antes de adentrarnos en la evolución de los algoritmos de hashing de contraseñas, es importante entender por qué es necesario.
Contraseñas en texto plano: Un gran riesgo de seguridad
Imagina que eres usuario de una página web en la que te has registrado con una cuenta. Un día, la web es hackeada y se filtra la base de datos. Si la web guarda las contraseñas en texto plano, el hacker puede acceder directamente a tu contraseña. Dado que muchas personas reutilizan las contraseñas en varias páginas web, el hacker puede usar esta contraseña para acceder a tus otras cuentas sin autorización. La situación empeora si usas la misma o una contraseña similar para tu cuenta de correo electrónico, ya que el hacker puede restablecer tu contraseña y hacerse con todas tus cuentas asociadas.
Incluso sin un fallo de seguridad en los datos, en equipos grandes, cualquier persona con acceso a la base de datos puede ver las contraseñas. En comparación con otra información, las contraseñas son muy sensibles y definitivamente no quieres que nadie tenga acceso a ellas.
Almacenar contraseñas sin hacerles hashing es un error amateur. Desafortunadamente, si buscas "filtración de contraseñas en texto plano", descubrirás que grandes corporaciones como Facebook, DailyQuiz, y GoDaddy han sufrido filtraciones de contraseñas en texto plano. Es probable que muchas otras empresas hayan cometido el mismo error.
Codificación vs. Cifrado vs. Hashing
Estos tres términos suelen confundirse, pero son conceptos distintos.
Codificación
La codificación es lo primero que se debe excluir para el almacenamiento de contraseñas. Por ejemplo, Base64 es un algoritmo de codificación que convierte datos binarios en una cadena de caracteres:
Conocer el algoritmo de codificación permite a cualquiera decodificar la cadena codificada y recuperar los datos originales:
Para los hackers, la mayoría de los algoritmos de codificación son equivalentes al texto plano.
Cifrado
Antes de que se popularizara el hashing, se utilizaba el cifrado para almacenar contraseñas, como con AES. El cifrado implica el uso de una clave (o un par de claves) para cifrar y descifrar datos.
El problema con el cifrado es evidente en el término "descifrar". El cifrado es reversible, lo que significa que si un hacker obtiene la clave, puede descifrar la contraseña y recuperar la contraseña en texto plano.
Hashing
La principal diferencia entre el hashing, la codificación y el cifrado es que el hashing es irreversible. Una vez que una contraseña se ha convertido en hash, no se puede descifrar a su forma original.
Como propietario de un sitio web, en realidad no necesitas conocer la contraseña en sí, siempre y cuando el usuario pueda iniciar sesión con la contraseña correcta. El proceso de registro se puede simplificar de la siguiente manera:
- El usuario introduce la contraseña.
- El servicio utiliza un algoritmo de hashing para calcular el valor hash de la contraseña.
- El servicio almacena el valor hash en la base de datos.
Cuando el usuario inicia sesión, el proceso es:
- El usuario introduce la contraseña.
- El servicio utiliza el mismo algoritmo de hashing para calcular el valor hash de la contraseña.
- El servicio compara el valor hash con el valor hash almacenado en la base de datos.
- Si los valores hash coinciden, se autentica al usuario.
Ambos procesos evitan almacenar contraseñas en texto plano, y dado que el hashing es irreversible, incluso si la base de datos es comprometida, el hacker solo puede obtener valores hash que aparecen como cadenas aleatorias.
Conjunto de algoritmos de hashing
El hashing puede parecer la solución perfecta para el almacenamiento de contraseñas, pero no es tan sencillo. Para entender por qué, exploremos la evolución de los algoritmos de hashing de contraseñas.
MD5
En 1992, Ron Rivest diseñó el algoritmo MD5, un algoritmo de resumen de mensajes que puede calcular un valor hash de 128 bits a partir de cualquier dato. MD5 ha sido ampliamente utilizado en varios campos, incluyendo el hashing de contraseñas. Por ejemplo, el valor hash MD5 de "123456" es:
Como se mencionó antes, el valor hash parece una cadena aleatoria y es irreversible. Además, MD5 es rápido y fácil de implementar, lo que lo convierte en el algoritmo de hashing de contraseñas más popular.
Sin embargo, las ventajas de MD5 también son sus debilidades para el hashing de contraseñas. Su velocidad lo hace vulnerable a los ataques de fuerza bruta. Si un hacker posee una lista de contraseñas comunes y tu información personal, pueden calcular el valor hash MD5 de cada combinación y compararlos con los valores hash en la base de datos. Por ejemplo, podrían combinar tu cumpleaños con tu nombre o el nombre de tu mascota.
En la actualidad, los ordenadores son significativamente más potentes que antes, facilitando la fuerza bruta de los hashes de contraseñas MD5.
Familia SHA
Entonces, ¿por qué no usar un algoritmo diferente que genera valores hash más largos? La familia SHA parece una buena elección. SHA-1 es un algoritmo de hashing que genera valores hash de 160 bits, y SHA-2 es una familia de algoritmos de hashing que generan valores hash de 224-bit, 256-bit, 384-bit, y 512 bits de longitud. Veamos el valor hash SHA-256 de "123456":
El valor hash SHA-256 es mucho más largo que el de MD5, y también es irreversible. Sin embargo, hay otro problema: si ya conoces el valor hash, como el anterior, y ves el mismo valor hash en la base de datos, sabes que la contraseña es "123456". Un hacker puede crear una lista de contraseñas comunes y sus valores hash correspondientes, y compararlos con los valores hash de la base de datos. Esta lista se conoce como una tabla arco iris.
Sal
Para mitigar los ataques de la tabla arco iris, se introdujo el concepto de sal. La sal es una cadena aleatoria que se añade a la contraseña antes de hacer el hashing. Por ejemplo, si la sal es "sal" y quieres usar SHA-256 para hacer el hash de la contraseña "123456" con la sal, en lugar de simplemente hacer:
Harías:
Como puedes ver, el resultado es completamente diferente al de hacer hash sin sal. Normalmente, a cada usuario se le asigna una sal aleatoria durante el registro, que se almacena en la base de datos junto con el valor hash. Durante el proceso de inicio de sesión, se usa la sal para calcular el valor hash de la contraseña introducida, que luego se compara con el valor hash almacenado.
Iteración
A pesar de la adición de la sal, el valor hash todavía es susceptible a los ataques de fuerza bruta a medida que el hardware se vuelve más potente. Para dificultarlo, se puede introducir la iteración (es decir, ejecutar el algoritmo de hashing varias veces). Por ejemplo, en lugar de usar:
Podrías usar:
Aumentar el número de iteraciones hace que la fuerza bruta sea más difícil. Sin embargo, esto también afecta al proceso de inicio de sesión, ya que se vuelve más lento. Por lo tanto, es necesario encontrar un equilibrio entre la seguridad y el rendimiento.
Descanso de medio tiempo
Hagamos un descanso y resumamos las características de un buen algoritmo de hashing de contraseñas:
- Irreversible (resistencia a la preimagen)
- Difícil de forzar brutalmente
- Resistente a los ataques de la tabla arco iris
Como habrás notado, la sal y la iteración son necesarias para satisfacer todos estos requisitos. El problema es que tanto MD5 como la familia SHA no fueron diseñados específicamente para el hashing de contraseñas; se utilizan ampliamente para hacer verificaciones de integridad (o "resumen de mensajes"). Como resultado, cada página web puede tener su propia implementación de sal e iteración, lo que dificulta la estandarización y la migración.
Algoritmos de hashing de contraseñas
Para resolver este problema, se han diseñado varios algoritmos de hashing específicos para el hashing de contraseñas. Veamos algunos de ellos.
bcrypt
bcrypt es un algoritmo de hashing de contraseñas diseñado por Niels Provos y David Mazières. Se utiliza ampliamente en muchos lenguajes de programación. Aquí tienes un ejemplo de valor hash de bcrypt:
Aunque parezca otra cadena aleatoria, contiene información adicional. Vamos a desglosarla:
- La primera sección
$2y
indica el algoritmo, que es2y
. - La segunda sección
$12
indica el número de iteraciones, que es12
. Esto significa que el algoritmo de hashing se ejecutará 212=4096 veces (iteraciones). - La tercera sección
wNt7lt/xf8wRJgPU7kK2ju
es la sal. - La última sección
GrirhHK4gdb0NiCRdsSoAxqQoNbiluu
es el valor hash.
bcrypt tiene algunas limitaciones:
- La longitud máxima de la contraseña es de 72 bytes.
- La sal está limitada a 16 bytes.
- El valor hash está limitado a 184 bits.
Argon2
Dadas las debates y las limitaciones de los algoritmos de hashing de contraseñas existentes, en 2015 se celebró una competencia de hashing de contraseñas. Saltándonos los detalles, vamos a centrarnos en el ganador: Argon2.
Argon2 es un algoritmo de hashing de contraseñas diseñado por Alex Biryukov, Daniel Dinu, y Dmitry Khovratovich. Introduce varios conceptos nuevos:
- Duro para memoria: El algoritmo está diseñado para ser difícil de paralelizar, lo que dificulta la fuerza bruta con GPUs.
- Duro para tiempo: El algoritmo está diseñado para ser difícil de optimizar, lo que hace difícil la fuerza bruta con ASICs (Circuitos integrados específicos de aplicación).
- Resistente a los ataques de canal lateral: El algoritmo está diseñado para ser resistente a los ataques de canal lateral, como los ataques de tiempo.
Hay dos versiones principales de Argon2, Argon2i y Argon2d. Argon2i es la más segura contra los ataques de canal lateral, mientras que Argon2d ofrece la mayor resistencia contra los ataques de cracking de GPU.
-- Argon2
Aquí tienes un ejemplo de un valor hash de Argon2:
Vamos a desglosarla:
- La primera sección
$argon2i
indica el algoritmo, que esargon2i
. - La segunda sección
$v=19
indica la versión, que es19
. - La tercera sección
$m=16,t=2,p=1
indica el costo de memoria, el costo de tiempo y el grado de paralelismo, que son16
,2
y1
. - La cuarta sección
$YTZ5ZnpXRWN5SlpjMHBDRQ
es la sal. - La última sección
$12oUmJ6xV5bIadzZHkuLTg
es el valor hash.
En Argon2, la longitud máxima de la contraseña es de 232-1 bytes, la sal está limitada a 232-1 bytes, y el valor hash está limitado a 232-1 bytes. Esto debería ser suficiente para la mayoría de los escenarios.
Ahora Argon2 está disponible en muchos lenguajes de programación, como node-argon2 para Node.js y argon2-cffi para Python.
Conclusión
A lo largo de los años, los algoritmos de hashing de contraseñas han experimentado una evolución significativa. Debemos agradecer a la comunidad de seguridad por sus décadas de esfuerzo en hacer de internet un lugar más seguro. Gracias a sus contribuciones, los desarrolladores pueden prestar más atención a la construcción de mejores servicios sin preocuparse por la seguridad del hashing de contraseñas. Aunque conseguir una seguridad del 100% en un sistema puede ser inalcanzable, podemos emplear diversas estrategias para minimizar los riesgos asociados.
Si deseas evitar el trabajo de implementar la autenticación y la autorización, no dudes en probar Logto de forma gratuita. Ofrecemos soluciones seguras (¡usamos Argon2!), fiables, y escalables, permitiéndote concentrarte en la construcción de tu producto.