HMAC - ¿Por qué no HMAC para el almacenamiento de contraseñas?

44

Nota bene: Soy consciente de que buena respuesta para asegurar el almacenamiento de contraseñas es scrypt o bcrypt . Esta pregunta no es para implementación en software real, es para mi propia comprensión.

Digamos que a Joe Programmer se le asigna la tarea de almacenar de forma segura las contraseñas de los usuarios finales en una base de datos para una aplicación web; o almacenar contraseñas en el disco para iniciar sesión en un software. Lo más probable es que él:

  1. Obtenga $password del usuario final.
  2. Cree $nonce como un valor aleatorio de unos 64 o 128 bits de tamaño.
  3. Cree $hash = SHA256($nonce$password) y almacene $nonce junto con $hash en la base de datos.

Pregunta uno:

¿Por qué el siguiente no es sustancialmente mejor que el anterior?

  1. Crea $long_string una vez y solo una vez. Almacene esto como una constante en el código de la aplicación. $long_string podría f.x. tener 2 kilobytes de caracteres aleatorios.
  2. Obtenga $password del usuario final.
  3. Cree $mac = HMAC-SHA256($long_string)[$password] (es decir, cree un MAC usando la contraseña del usuario final como clave) y almacene este $mac en la base de datos.

Me imagino que la HMAC tiene los siguientes beneficios:

  • ¿Las colisiones son menos frecuentes?
  • ¿Es computacionalmente algo más costoso que el hashing simple? (Pero no en ningún lugar cerca de Scrypt, por supuesto).
  • Para tener éxito con un ataque de fuerza bruta en un tiempo razonable, y el atacante necesitaría acceder a dos cosas: 1) la base de datos, donde se almacena $mac y < em> 2) el código de la aplicación, donde se almacena el $long_string original. Ese es uno mejor que una función hash, donde el atacante solo necesita acceso a la base de datos.

Pero aún así, nadie parece sugerir el uso de un HMAC, ¿así que debo estar entendiendo mal algo?

Pregunta dos:

¿Cuáles serían las implicaciones de agregar un valor de sal $nonce ?

  1. Crea $long_string una vez y solo una vez. Almacene esto como una constante en el código de la aplicación.
  2. Obtenga $password del usuario final.
  3. Cree $nonce como un valor aleatorio de aproximadamente 128 bits de tamaño.
  4. Cree $mac = HMAC-SHA256($long_string)[$nonce$password] y almacene $nonce y $mac en la base de datos.
pregunta Jesper Mortensen 19.04.2011 - 14:09
fuente

2 respuestas

33

El objetivo de la sal es evitar el costo compartido del ataque: si un atacante quiere atacar dos contraseñas, entonces debería ser el doble de costoso que atacar una sola contraseña.

Con su propuesta (su "pregunta 1"), dos usuarios con la misma contraseña terminarán usando el mismo MAC. Si un atacante tiene acceso de lectura a su base de datos, puede "probar" las contraseñas (volviendo a calcular el MAC) y buscar la coincidencia en la base de datos. Luego puede atacar todas las contraseñas en paralelo, por el costo de atacar una. Si su long_string es una constante codificada en el código fuente de la aplicación, entonces todas las instancias instaladas de la aplicación comparten esta constante, y vale la pena (para el atacante) calcular un gran diccionario de pares de contraseña a MAC, también conocido como "una mesa del arco iris".

Con un nonce (su "pregunta 2") usted evita el costo compartido. El nonce se conoce generalmente como una "sal". Tu long_string y el uso de HMAC no te compran mucho aquí (y no estás usando HMAC para lo que fue diseñado, por cierto, por lo que estás en cimientos inestables, criptográficamente hablando). Usar una sal es una muy buena idea (bueno, no usar una sal es una muy mala idea, al menos) pero hace solo la mitad del trabajo. También debe tener un procedimiento de hashing lento . El punto aquí es que la sal evita el costo compartido, pero no evita atacar una sola contraseña. Atacar una contraseña significa probar posibles contraseñas hasta que una coincida (ese es el "ataque de diccionario") y, dada la imaginación del usuario humano promedio, los ataques de diccionario tienden a funcionar: la gente simplemente ama usando contraseñas que pueden ser adivinado La solución consiste en utilizar un proceso de hash que es inherentemente lento, generalmente mediante la iteración de la función de hash unos pocos miles de veces. La idea es hacer que la verificación de la contraseña sea más costosa: hacer que el usuario espere 1 ms en lugar de 1µs no supone ningún problema (el usuario no lo notará), pero también hará que el ataque del diccionario sea 1000 veces más caro. Su long_string puede usarse para eso, siempre que sea realmente largo (no 2 kilobytes, en lugar de 20 megabytes).

HMAC puede usarse en lugar de una función hash sin procesar para fortalecer un sistema de verificación de contraseña, pero en una configuración diferente. Dado un sistema que comprueba contraseñas con sales y funciones hash iteradas, puede reemplazar la función hash con HMAC, utilizando una clave secreta K . Esto evita los ataques de diccionario fuera de línea siempre que pueda mantener en secreto K . Mantener un valor secreto no es fácil, pero aún así es más fácil mantener un secreto K de 128 bits que una base de datos completa.

    
respondido por el Thomas Pornin 19.04.2011 - 15:00
fuente
3

HMAC no te compra mucho aquí, aunque supongo que actúa como una función hash reemplazable (reemplazable cambiando la clave). De todos modos, aquí parece una exageración.

Sin embargo, el primer esquema que propones anteriormente falla catastróficamente si $ long_string es conocido por un atacante, lo que probablemente será ya que el código probablemente no se considera secreto (y, en general, es una mala práctica confiar en el secreto del código de todos modos ).

Debería usar el segundo esquema, ya que $ nonce es necesario para protegerse contra ataques precomputados.

    
respondido por el frankodwyer 19.04.2011 - 14:54
fuente

Lea otras preguntas en las etiquetas