FechaVersiónDescripción
16/05/20221.0.0Cifrado de contraseñas seguro con Argon2
28/03/20252.0.0Actualización de dependencias

Cifrado de contraseñas con Argon2

En este pequeño documento, vamos a ver cuales son los motivos por los que es necesario llevar a cabo un cifrado seguro de las contraseñas que almacenamos en la base de datos.

 

1. ¿Qué es el Hasing?

Hashing es el proceso de generar una cadena, o hash, a partir de un mensaje determinado mediante una función matemática conocida como función hash criptográfica.

Si bien existen varias funciones de hash, las que se adaptan a las contraseñas de hash deben tener cuatro propiedades principales para ser seguras:

  1. Debe ser determinista: el mismo mensaje procesado por la misma función hash siempre debe producir el mismo hash.

  2. No es reversible: no es posible generar un mensaje a partir de su hash.

  3. Tiene una entropía alta: un pequeño cambio en un mensaje debería producir un hash muy diferente.

  4. Y resiste colisiones: dos mensajes diferentes no deberían producir el mismo hash.

Una función hash que tiene las cuatro propiedades es una fuerte candidata para el hash de contraseñas, ya que juntas aumentan drásticamente la dificultad de realizar ingeniería inversa para tratar de obtener la contraseña a partir del hash.

 

Además, sin embargo, las funciones de hashing de contraseñas deberían ser lentas. Un algoritmo rápido ayudaría a los ataques de fuerza bruta en los que un hacker intentará adivinar una contraseña mediante el hash y la comparación de miles de millones (o trillones) de contraseñas potenciales por segundo.

Algunas excelentes funciones hash que cumplen con todos estos criterios son PBKDF2, BCrypt , SCrypt. y Argon2. Pero primero, echemos un vistazo a algunos algoritmos más antiguos y por qué ya no se recomiendan.

 

2. MD5. No recomendado.

Nuestra primera función hash es el algoritmo MD5 MessageDigest , desarrollado en 1992.

MessageDigest de Java hace que esto sea fácil de calcular y aún puede ser útil en otras circunstancias.

Sin embargo, en los últimos años, se descubrió que MD5 fallaba en la cuarta propiedad de hashing de contraseñas, ya que se volvió computacionalmente fácil generar colisiones. Para colmo, MD5 es un algoritmo rápido y, por lo tanto, inútil contra ataques de fuerza bruta.

Debido a esto, no se recomienda MD5.

 

3. SHA-512. No recomendado.

A continuación, veremos SHA-512, que forma parte de la familia Secure Hash Algorithm, una familia que comenzó con SHA-0 en 1993.

 

3.1. ¿Por qué SHA-512?

A medida que aumenta la potencia de las computadoras y encontramos nuevas vulnerabilidades, los investigadores derivan nuevas versiones de SHA. Las versiones más nuevas tienen una longitud progresivamente más larga o, a veces, los investigadores publican una nueva versión del algoritmo subyacente.

SHA-512 representa la clave más larga en la tercera generación del algoritmo.

Si bien ahora hay versiones más seguras de SHA, SHA-512 es la más fuerte que se implementa en Java.

3.2. Implementación en Java

Ahora, echemos un vistazo a la implementación del algoritmo hash SHA-512 en Java.

Primero, tenemos que entender el concepto de sal. En pocas palabras, esta es una secuencia aleatoria que se genera para cada nuevo hash.

Al introducir esta aleatoriedad, aumentamos la entropía del hash y protegemos nuestra base de datos contra listas precompiladas de hash conocidas como tablas arcoíris.

Nuestra nueva función hash se vuelve aproximadamente:

3.3. Generando Sal

Para introducir sal, usaremos la clase SecureRandom de java.security:

Luego, usaremos la clase MessageDigest para configurar la función hash SHA-512 con nuestra sal:

Y con eso añadido, ahora podemos usar el método de resumen para generar nuestra contraseña cifrada:

3.4. ¿Por qué no es recomendado?

Cuando se emplea con sal, SHA-512 sigue siendo una buena opción, pero existen opciones más potentes y más lentas.

 

4. Hash de contraseñas Java con Argon2

Argon2 fue el ganador de Password Hashing Competition en julio de 2015, una función de hash unidireccional que intencionalmente requiere muchos recursos (CPU, memoria, etc.). En Argon2, podemos configurar la longitud de la sal, la longitud del hash generado, las iteraciones, el costo de la memoria y el costo de la CPU para controlar los recursos que se necesitan para codificar una contraseña.

El algoritmo Argon2 tiene tres variantes:

El algoritmo Argon2 acepta cinco parámetros configurables:

 

4.1. ¿Por qué necesitamos un hash de contraseña lento?

El hardware moderno (CPU y GPU) es cada vez mejor y más barato. La CPU de usuario, como AMD Ryzen Threadripper, aumenta los núcleos cada año, por ejemplo, AMD Ryzen™ Threadripper™ PRO 7995WX tiene 96 núcleos, 192 subprocesos.

Además, gracias al auge de la minería de criptomonedas, la GPU sigue evolucionando, la FPGA o el ASIC dedicado pueden realizar miles de millones de cálculos de hash por segundo.

La computación cuántica aún es demasiado pronto para mencionarla, pero tarde o temprano entraremos en la era de la computación cuántica, y nadie sabe cuánto más rápido pueden funcionar las computadoras cuánticas a la velocidad hash.

Ley de Moore, pronostica un avance rápido en diez años, la velocidad del hash crecerá exponencialmente, es muy posible que podamos decodificar una contraseña MD5, SHA1, SHA-256, SHA-512 o BLAKE2 en un segundo o incluso más rápido (incluso sin necesidad de salar).

En pocas palabras, todos los algoritmos hash rápidos no son adecuados para el hash de contraseñas, y necesitamos algunos algoritmos hash lentos y de uso intensivo de recursos como Bcrypt, Scrypt o Argon2.

 

5. Hash de contraseña Java Argon2 – argon2-jvm

Para poder hacer uso de este vamos a necesitar configurar la librería de Maven, por lo tanto tendremos que añadir al pom.xml la siguiente dependencia:

El Argon2Factory.create() predeterminado devuelve una variante de argon2i, con 16 bytes de sal y 32 bytes de longitud de hash.

En este ejemplo se dan las siguientes características:

  1. Variante= argon2i (default)

  2. Sal= 16 bytes, 128-bit (default)

  3. Longitud del Hash = 32 bytes, 256-bit (default)

  4. Iteraciones= 10

  5. Memoria= 65536k, 64M

  6. Paralelismo= 1

 

¿Cómo podemos cifrar una contraseña con Argon2?

Lo primero que haremos en la clase donde vayamos a utilizar Argon2 es añadir los siguientes import (antes habremos añadido la dependencia de Maven):

Una vez tenemos los import realizamos lo siguiente:

Esto nos mostrará el hash generado, algo similar a lo siguiente:

Entendemos que el método hash nos genera el hash del password.

A partir de p= podemos ver el hash del password. Si lo ejecutamos en varias ocasiones nos dará un hash diferente.

Este es el que almacenaríamos en la BD:

 

¿Cómo comparamos si este es correcto?

Utilizaremos el siguiente método que nos ofrece:

Esta función es booleana, devuelve true si los hash hacen match y devuelve false si no hacen match.

 

Dejo un fragmento de código para que se pueda ver: