Teórico - Contraseña de salado con concatenación vs. salado con HMAC

-1

Estaba buscando diferentes métodos de salado e intenté comparar cuál es más seguro para el almacenamiento de contraseñas. Sé que HMAC no fue pensado al principio para el hashing de contraseñas pero se usa ampliamente para ese propósito. También sé que el método más seguro es usar el estiramiento de las teclas. Miré a través de CrackStation , los antiguos no hash secretos article , pregunta en este tema y 3 formas incorrectas de almacenar contraseñas artículo . Todavía no pude concluir cuál de los siguientes métodos es más seguro que el otro y por qué:

  • sha256 ($ pass. $ salt)
  • sha256 ($ sal. $ pase)
  • HMAC-SHA256 (clave = $ pass, $ salt)
  • HMAC-SHA256 (key = $ salt, $ pass)

ADVERTENCIA: Ninguno de estos es seguro para el almacenamiento de contraseñas. La pregunta es acerca de las diferencias teóricas marginales entre estos métodos de hashing de contraseña y salazón.

Estaba considerando ataques de extensión de longitud que probablemente no son un factor aquí, o son (lo que haría que HMAC sea más seguro). Entonces, ¿quizás también HMAC sería menos seguro porque se conoce la clave? ¿Cómo clasificarías esos métodos por seguridad?

    
pregunta fsacer 24.04.2017 - 21:29
fuente

4 respuestas

-1

Según una investigación adicional, supongo que el orden sería y la conversación en reddit :

(más seguro)

  1. HMAC-SHA256 (key = $ salt, $ pass): es mejor porque no tiene inconvenientes para limitar la longitud de la contraseña, también es favorable porque key es más aleatorio, lo que hace que el hash sea aún más aleatorio y no Hay que sanear la contraseña en algunos casos (gracias a @Nat)
  2. HMAC-SHA256 (clave = $ pass, $ salt) - longitud de contraseña limitada de forma posiblly basado en la implementación de sha256-hmac, más seguro debido a un factor de trabajo un poco más alto
  3. sha256 ($ pass. $ salt): en base a algunos aspectos específicos de la implementación de SHA, es más seguro agregar sal
  4. sha256 ($ sal. $ pase): no se agrega la sal como tan segura

(menos seguro)

Descargo de responsabilidad: esta diferencia es más de una naturaleza teórica y en la práctica no debe usar ninguno de estos métodos, sino usar cualquiera de estos se expresaron en la respuesta de Stephen.

    
respondido por el fsacer 25.04.2017 - 21:22
fuente
2

(Ninguno de los ejemplos que proporcione debe usarse para el hashing de contraseñas. ¡Son demasiado rápidos!)

El primer principio general que mencionaría aquí es que queremos que nuestras operaciones criptográficas sean interfaces de alto nivel ; debe haber una abstracción reutilizable y común que defina:

  • Qué condiciones previas debe cumplir la persona que llama antes de llamarla;
  • Qué argumentos toma la operación;
  • qué valores devuelve o qué condiciones posteriores cumple si se llama correctamente;
  • Qué propiedades de seguridad debe ofrecer el concepto.

En este caso, estamos hablando de hash de contraseña . Una función de hashing de contraseñas, en su encarnación más simple, debería:

  • Acepta dos argumentos, un salt y una contraseña;
  • Devuelva un código hash para esa combinación de contraseña / sal;
  • Inherentemente, consume una gran cantidad de recursos para que ralentice sustancialmente a un atacante que adivina la contraseña, pero no tanto como un usuario honesto

Entonces SHA-256 falla el primer y tercer punto. ¡Solo acepta un argumento, y es demasiado rápido!

Y, de hecho, la interfaz descrita anteriormente no es la mejor para el hashing de contraseñas. Una mejor interfaz es como un par de funciones escritas encima del hash de contraseña (en pseudocódigo Python-ish):

def generate_new_verification_code(password):
    salt = crypto_random(16)     # 16 byte random salt
    hash = password_hash(salt, password)
    verification_code = salt + hash
    return verification_code

def verify_password(putative_password, actual_verification_code):
    actual_salt = actual_verification_code[0:16]
    actual_hash = actual_verification_code[16:-1]
    putative_hash = password_hash(actual_salt, putative_password)
    return putative_hash == actual_hash

Por lo tanto, la abstracción adecuada dice que para la verificación de la contraseña deberíamos estar usando dos funciones como estas, escritas sobre una función password_hash de uso intensivo de recursos, que a su vez probablemente se escribiría sobre una función de bajo nivel como SHA -256.

Tenga en cuenta que la interfaz de hash de contraseña de PHP sigue este último diseño:

  • La función password_hash debe llamarse con una contraseña pero sin sal ; escoge una sal al azar, y como dice la página: "El algoritmo, el costo y la sal utilizados se devuelven como parte del hash. Por lo tanto, toda la información que se necesita para verificar el hash se incluye en ella".
  • La función password_verify se usa para verificar si una contraseña putativa coincide con la cadena de verificación .

Segundo problema: una idea clave en el diseño de protocolos de seguridad u otras construcciones de seguridad es que los atacantes a menudo romperán las reglas y harán que su código sea llamado de maneras y contextos inesperados . Por ejemplo, cuando la gente escribe algo como esto:

hash($salt.$pass)
hash($pass.$salt)

... a menudo asumen implícitamente que salt siempre tendrá la misma longitud fija, y no se detienen a considerar si algún atacante podría:

  1. Encuentre una manera de "romper las reglas" para que puedan causar que la longitud de salt varíe a lo largo de varias llamadas al código;
  2. Encuentre alguna forma inesperada de explotar esto para su ventaja.

Por ejemplo, podrías pensar que es imposible que un atacante encuentre una colisión para las contraseñas de esta manera, pero de hecho, si pueden controlar el salt y el pass , es trivial hacerlo:

hash("0001"."Passsword1!")
hash("0001P"."asssword1!")

Esto puede sonar descabellado, y bueno, para decirte la verdad, no puedo pensar en un escenario en el que esto pueda explotarse. Pero realmente nos gustaría que nuestra seguridad se fundara por razones más sólidas que "No se me ocurre ninguna manera de romper esto". Entonces, como filosofía general, es más seguro diseñar las cosas de tal manera que no puedan surgir ambigüedades. En este caso, nos gustaría que se cumpla la siguiente regla:

  • Si hash dos contraseñas diferentes con dos sales diferentes, las entradas que proporcionamos a la función hash de bajo nivel deberían ser diferentes.

Esto significa que para generar la entrada que alimentamos al hash subyacente, deberíamos preferir combine con un inyectable función : una función tal que combine(salt1, password1) == combine(salt2, password2) si y solo si salt1 == salt2 y password1 == password2 .

hash(combine($salt, $password))

Algunas formas de hacer esto:

  • Ponga una de las entradas en un tamaño fijo y antepóngala a la otra. HMAC hace esto internamente para las llaves.
  • Hash una de las dos entradas y antepónelo a la otra. HMAC hace esto para las claves que son más largas que el tamaño de bloque de la función hash subyacente.
  • Coloque un delimitador no ambiguo entre las entradas (posiblemente requiera que escape las incidencias del delimitador en los valores de entrada).

Funciones de hash de contraseña dedicadas como PBKDF2, bcrypt, scrypt y Argon2 se adhieren a la mayoría de estas ideas.

    
respondido por el Luis Casillas 27.04.2017 - 07:29
fuente
2

Los cuatro métodos que enumeró son esencialmente equivalentes seguros con el fin de hashear las contraseñas.

La construcción HMAC de H(k1 ++ H(k2 ++ m)) (donde ++ es concatenación) está diseñada específicamente para evitar que los ataques contra MAC se utilicen para autenticar los mensajes escritos por alguien que posee la clave secreta ( k1, k2 ; donde k1 = K ⊕ opad , k2 = K ⊕ ipad , donde opad=0x5c5c...5c y ipad=0x3636...36 ). Debido a que las funciones hash comunes como md5, sha1, sha2 son vulnerables a los ataques de extensión de longitud, no se puede usar una construcción como H(k ++ m) como MAC, ya que un intruso que observó un mensaje firmado m podría usar una extensión de longitud ataque para hacer un MAC válido para un mensaje m' = m ++ tampered_msg . De manera similar, la construcción H(m ++ k) también es insegura si un atacante podría generar una colisión (encuentre dos mensajes H(m1) = H(m2) donde m1 y m2 se alinearon en un bloque) y luego si puede engañar a alguien para que cree un MAC para m1 , puede usar eso como un MAC válido para m2 .

Sin embargo, ser susceptible a ataques de extensión de longitud o precolisión no es relevante para el uso de almacenamiento y validación de contraseñas de usuarios. Todo lo que es relevante es la longitud del hash devuelto, el hecho de que el salt es único para cada contraseña almacenada en su sistema (por lo que no se atacan varias contraseñas en paralelo, el tiempo para calcular el hash (los hashes intrínsecamente lentos son más seguros contra los brutos). fuerza), y la fuerza de la contraseña.

Si uso H(random_salt ++ password) con un hash que es vulnerable a los ataques de extensión de longitud, no hay una vulnerabilidad equivalente. ¿A quién le importa si alguien que no conoce mi contraseña pero que ha visto el hash podría potencialmente calcular H(random_salt ++ password ++ tampered_pw) para cualquier tampered_pw que quiera agregar a mi contraseña sin averiguar qué es password ?

Dicho esto, la construcción HMAC puede ser un poco más segura, ya que llama a la función hash dos veces, por lo que, en teoría, debería tomar el doble de tiempo que la fuerza bruta; aunque normalmente cuando se realizan varias rondas de hash, se realizan muchos miles de rondas de hash (lo que hace que la fuerza bruta sea muchas veces más difícil) en lugar de solo 2 rondas de hashing.

    
respondido por el dr jimbob 27.04.2017 - 08:41
fuente
1

HMAC, por sí mismo, no es un hash de contraseña apropiado. Está trabajando a un nivel demasiado bajo para su experiencia en criptografía. Ya existen construcciones diseñadas específicamente para la verificación infalible de contraseñas; ¡usalos, usalos a ellos! Elija uno de bcrypt , scrypt , o Argon2 .

    
respondido por el Stephen Touset 25.04.2017 - 01:27
fuente

Lea otras preguntas en las etiquetas