Libera tu mente y alcanza tus metas
Qué método de hashing utilizar en aplicaciones modernas (bCrypt, sCrypt, PBKDF2, Argon2)
Qué método de hashing utilizar en aplicaciones modernas (bCrypt, sCrypt, PBKDF2, Argon2)

Qué método de hashing utilizar en aplicaciones modernas (bCrypt, sCrypt, PBKDF2, Argon2)

Si bien todas estas funciones de hash actuales son bastante seguras, dada una implementación adecuada y buenos parámetros de costo, quería dar una retrospectiva más amplia sobre la seguridad real de estas funciones.

Hash

El proceso hash es una función unidireccional que, dados algunos datos, los convierte en una cadena única y de longitud fija. Y ese proceso siempre devolverá esa cadena "hasheada" para los mismos datos en texto plano. Esto permite que un sistema verifique la validez de una contraseña sin conocimiento de los datos originales.

El hashing resuelve el problema del acceso inmediato al sistema con contraseñas expuestas. Si los datos están hasheados, es muy difícil volver a convertir el hash a la entrada original y también es muy difícil encontrar la entrada para obtener el resultado deseado.

Tenemos que utilizar hash de la contraseña en dos casos:

  • Cuando el usuario se registra en la aplicación, codificamos la contraseña para guardarla en la base de datos.
  • Cuando el usuario quiere autenticarse, hacemos un hash de la contraseña proporcionada y la comparamos con el hash de la contraseña almacenada de la base de datos.

Ahora, cuando los atacantes obtienen el hash de una contraseña, no pueden usarla para acceder al sistema. Cualquier intento de encontrar el texto sin formato a partir del valor hash requiere un gran esfuerzo por parte del atacante. Un ataque de fuerza bruta puede ser muy costoso si el hash es lo suficientemente largo (o tiene otras propiedades).

Sin embargo, al usar tablas Rainbow, los atacantes pueden tener éxito relativamente "rápido". Una tabla arcoíris es una tabla con hashes precalculados para muchas contraseñas (miles de millones). Hay muchas tablas de arcoíris disponibles en Internet y muchos algoritmos conocidos.

Agregar SALT a la contraseña

Para prevenir un ataque con las tablas del arco iris, podemos usar contraseñas "salteadas". Un SALT es una secuencia de bytes generados aleatoriamente que se codifican junto con la contraseña (o se guardan aparte). La "sal" se puede almacenar y no necesita ser protegida.

Cada vez que el usuario intenta autenticarse, la contraseña del usuario se codifica con el SALT guardado y el resultado debe coincidir con la contraseña almacenada. La probabilidad de que la combinación de la contraseña y el SALT se calcule previamente en una tabla de arco iris es muy pequeña. Si la sal agregada es lo suficientemente larga y aleatoria, es (casi) imposible encontrar el hash en una tabla de arcoíris.

Sin embargo, dado que el SALT no es un secreto, los atacantes aún pueden iniciar un ataque de fuerza bruta utilizando el hash y el SALT obtenidos. El SALT sal puede dificultar el ataque, pero el hardware se está volviendo más eficiente. Debemos asumir que un hardware de rápida evolución con el que el atacante puede calcular miles de millones de hashes por segundo.

Nota: algunas funciones hash tienen colisiones, lo que significa que diferentes datos pueden dar como resultado el mismo resultado.

Funciones de hashing de contraseña

Las funciones hash no se crearon para cifrar solo las contraseñas. El "inventor" de las funciones hash hizo un muy buen trabajo e hizo que las funciones de hash fueran muy rápidos. Sin embargo, si nosotros podemos codificar contraseñas muy rápido, entonces un atacante también puede ejecutar el ataque de fuerza bruta muy rápido.

La solución es hacer que el hashing de contraseñas sea "lento" (hablando de ciclos de reloj y en millonésimas de segundo). Pero, ¿qué tan lento puede ser? No debe ser tan lento como para ser inaceptable para el usuario, pero lo suficientemente lento como para hacer que un ataque de fuerza bruta tome un tiempo "infinito".

Se han desarrollado varios algoritmos especialmente para el hashing de contraseñas:

  • bCrypt
  • sCrypt
  • PBKDF2
  • Argon2
  • otros...

Estas funciones tienen un algoritmo criptográfico complicado y asignan recursos como CPU o memoria deliberadamente para hacerlos más lentos.

También se podría considerar usar PEPPER para proporcionar una defensa adicional en profundidad. Se puede usar "pimienta" para evitar que un atacante pueda descifrar cualquiera de los hashes si solo tiene acceso a la base de datos, por ejemplo, si ha explotado una vulnerabilidad de inyección SQL u obtenido una copia de seguridad de la base de datos.

Ataques

Dado un atacante aleatorio que puede volcar/leer/obtener todas las contraseñas de los usuarios, tendría que:

  • Determinar la función hash que genera esa salida.
  • Si tiene la suerte de tener hashes MD5/SHA1 (o algo similar), tendrás un atacante feliz.
  • Si el desarrollador de la aplicación usó una función de hashing lenta, las cosas podrían complicarse un poco más.
  • Podría ejecutar un análisis estadístico para encontrar una lista parcial de contraseñas. Podría ejecutar una fuerza bruta sobre ellas.

Entonces, en teoría, si un atacante está dispuesto (está medio loco y tiene los recursos necesarios) podría descifrar todas las contraseñas por fuerza bruta. Lo más común es que el atacante utilice hardware especializado como ASIC y FPGA para realizar estas tareas.

ASIC y FPGA son hardware muy optimizado para realizar UNA tarea; un ASIC es un circuito estático (lo que significa que el hardware está ESTRICTAMENTE construido para ese algoritmo), un FPGA se puede reprogramar.

PBKDF2

PBKDF2 es una función bastante sencilla: realiza el HMAC tantas veces como se especifica en el parámetro 'iteraciones'. Esto no se ve tan bien si el atacante posee una GPU o equipos de GPU decentes, ya que están diseñados de una manera diferente: tienen múltiples núcleos que brillan cuando se necesita trabajar en paralelo con una gran cantidad de datos. Cada núcleo puede ejecutar una instrucción contra miles y miles de datos al mismo tiempo. Si bien PBKDF2 es muy duro de romper en una CPU, es un trabajo bastante fácil para un sistema de GPU.

BCrypt

BCrypt es de 1999 y es resistente por diseño a ataques de GPU-ASIC, ya que también es una función de refuerzo de la memoria: no solo requiere un uso intensivo de la CPU, sino también de la RAM para ejecutar un hash de bCrypt.

Sin embargo, los tiempos han cambiado y un atacante sofisticado y quizás rico utilizará una FPGA grande y potente, y los modelos contemporáneos ahora tienen bloques de RAM integrados, que optimizan en gran medida este trabajo. Entonces, si bien bCrypt hace un buen trabajo al dificultar la vida de un atacante ASIC, hace poco contra uno FPGA.

SCrypt

Scrypt soluciona esto desde 2009 ya que no solo usa tiempo exponencial, sino también memoria exponencial.

Argon2

Argon2 ganó la competencia Password-Hashing (PHC) en julio de 2015. La competencia se inició en el otoño de 2012, en el primer trimestre de 2013, la junta publicó una convocatoria de presentaciones y la fecha límite fue a fines de marzo de 2014. Como parte de la competencia, se revisaron minuciosamente las presentaciones y publicaron un breve informe inicial donde describen su selección, criterio y justificación.

Hay dos versiones principales de Argon2: Argon2i, que es la opción más segura contra los ataques de canal lateral, y Argon2d, que es la opción más segura contra los ataques de craqueo de GPU. El código fuente está disponible en GitHub, escrito en C y compilado en la mayoría de las arquitecturas ARM, x86 y x64.

Ambas versiones del algoritmo se pueden parametrizar mediante:

  • Un costo de tiempo, que define el tiempo de ejecución.
  • Un costo de memoria, que define el uso de la memoria
  • Un grado de paralelismo, que define el número de subprocesos

Esto significa que puede ajustar estos parámetros por separado y adaptar el límite de seguridad a cada caso de uso, modelo de amenaza y especificaciones de hardware.

Además de esto, Argon2 es particularmente resistente a los ataques de compensación de clasificación, lo que hace que sea mucho más difícil optimizar económicamente en FPGA: aunque los FPGA recientes tienen bloques de RAM integrados, el ancho de banda de la memoria sigue siendo una limitación.

¿Qué se considera seguro hoy?

A lo dicho previamente, la carrera hacia las criptomonedas le agregó otra faceta: con capitalizaciones de mercado de miles de millones de dólares, la implementación de software/hardware más rápida de los algoritmos criptográficos subyacente en cada criptomoneda.

Si bien Bitcoin usa SHA256 como la función criptográfica que puede optimizarse en gran medida para utilizar hardware, otros creadores han tratado de hacer que las nuevas criptomonedas sean más justas para la minería confiando en el uso de la memoria: Litecoin utiliza sCrypt y Zcash utiliza Equihash.

Esto significa que las mismas funciones lentas que se utilizan para generar hashes de contraseñas se utilizan para proteger miles de millones de dólares en criptomonedas. El principio sigue siendo el mismo: necesitamos una función "lenta" que haya sido examinada públicamente por la comunidad criptográfica y permanezca intacta.

PBKDF2 ha existido durante mucho tiempo y no ha envejecido muy bien: se puede paralelizar fácilmente en sistemas multinúcleo (GPU) y es trivial para sistemas personalizados (FPGA/ASIC). Así que actualmente es un NO de mi parte.

BCrypt ha estado disponible desde 1999 y hace un mejor trabajo al ser resistente a GPU/ASIC que PBKDF2, pero no lo recomendaría para aplicaciones modernas ya que no destaca en un modelo de amenaza con cracking fuera de línea. Si bien algunas criptomonedas se basan en él, no ha ganado mucha popularidad y, en consecuencia, no ha ganado suficiente interés por parte de la comunidad FPGA/ASIC para construir una implementación de hardware.

Dicho esto, Solar Designer (OpenWall), Malvoni y Knezovic (Universidad de Zagreb) escribieron un artículo en 2014 que describe un sistema híbrido de ARM/FPGA para atacar el algoritmo.

SCrypt es una mejor opción hoy porque tiene un mejor diseño que bCrypt (especialmente en lo que respecta a la dureza en memoria) y ha estado en el campo durante 10 años. Por otro lado, se ha utilizado para muchas criptomonedas y tenemos algunas implementaciones de hardware (tanto FPGA como ASIC).

Esta hoja de trucos de OWASP proporciona orientación sobre las diversas áreas que deben tenerse en cuenta en relación con el almacenamiento de contraseñas. En breve:

  • Utilizar Argon2id con una configuración mínima de 19 MiB de memoria, 2 iteraciones y 1 grado de paralelismo.
  • Si Argon2id no está disponible, usar sCrypt con un parámetro de costo mínimo de CPU/memoria de (2^17), un tamaño de bloque mínimo de 8 (1024 bytes) y un parámetro de paralelización de 1.
  • Para sistemas heredados que usan bCrypt, usar un factor de trabajo de 10 o más y con un límite de contraseña de 72 bytes.
  • Si se requiere el cumplimiento de FIPS-140, usar PBKDF2 con un factor de trabajo de 600.000 o más y configurarlo con una función de hash interna de HMAC-SHA-256.

Por lo expuesto, no recomendaría utilizar PBKDF2 o bCrypt para aplicaciones modernas y recomiendo Argon2 (preferiblemente Argon2id) para los sistemas más nuevos. Scrypt puede ser una segunda opción en sistemas donde Argon2 no está disponible.

Fuentes: @mpreziuso | @analytics-vidhya | Synkre