¿Por qué es tan malo improvisar su propia función Hash con las funciones hash existentes?

72

Me temo que me tirarán tomates por hacer esta vieja pregunta, pero aquí va.

Después de leer, es peligroso preparar su propio hash de contraseña para las funciones de hash existentes "over y over otra vez, todavía no No entiendo la lógica. Aquí hay algunos ejemplos:

  • md5(md5(salt) + bcrypt(password))
  • scrypt(bcrypt(password + salt))
  • sha1(md5(scrypt(password + md5(salt))))

Los argumentos típicos en contra de estos son los siguientes:

  

¡No eres un criptógrafo! No tienes idea si estos hashes son más seguros. Déjelo a los expertos que saben lo que están haciendo. Estos no agregan seguridad extra.

Por supuesto, no mejoran la función como un hash (es decir, hacen que sea más difícil revertir o encontrar colisiones, etc.), pero seguramente seguramente no hacen es worse como un hash? Si lo hicieran, los piratas informáticos podrían volver a aplicar hash de forma estándar a las contraseñas hash en estos hashes extraños, ya que consideran que están en forma y debilitan el hash. No lo compro.

Segundo argumento:

  

Principio de Kerckoffs : un criptosistema debe ser seguro incluso si se conoce todo sobre el sistema.

De acuerdo. Esta es básicamente la motivación para no almacenar sus contraseñas como texto simple en primer lugar. Pero si mi respuesta a la primera crítica se mantiene, estos hash extravagantes todavía funcionan como hash seguros, y nuestro sistema no rompe el principio de Kerckoffs más de lo que lo haría con un hash estándar.

Aquí hay dos posibles (y vale la pena, por lo que puedo ver) las ventajas de usar un hash "raro" sobre un hash normal:

  1. Claro, su sistema debería estar seguro si el atacante tiene el código fuente, pero es muy probable que su atacante no tenga acceso a su código fuente y probablemente no podrá adivinar su hash loco, haciendo imposible cualquier intento de fuerza bruta.
  2. (Esta es la motivación real detrás de mí que hace esta pregunta) Se cree que BCrypt es seguro, difícil para la CPU y la GPU (excelente) pero puede ser muy rápido con hardware especializado . Se dice que SCrypt es difícil de imponer a la fuerza bruta en las CPU, GPU y actualmente está disponible en especial, pero es más reciente y la comunidad criptográfica no confía tanto como BCrypt debido a la falta de exposición que ha tenido. Pero, ¿el hash BCrypt(SCrypt(password + salt)) no obtiene lo mejor de ambos mundos?

Aprecio que la pasión / la ira detrás de la mayoría de las críticas en contra de estos hashes de fabricación casera provienen de la falta de conocimiento del programador promedio de lo que hace un buen hash, y la preocupación de que alentar este tipo de hashing loco terminará inevitablemente Hashes débiles e inútiles que ingresan al código de producción. Pero si el hash loco se construye cuidadosamente a partir de hashes sólidos y confiables, ¿las ganancias en seguridad no son muy valiosas y reales?

Actualizar

Tengo un montón de buenas respuestas sobre esto, gracias. Lo que parecía ignorar en mis suposiciones era que, aunque la combinación de hashes no puede hacer que sea más fácil descifrar la contraseña original y, por lo tanto, descifrar los hashes constituyentes, la combinación de dos o más hashes seguros puede: al menos en principio: ser más débil que cualquiera de sus hashes internos debido a las interacciones no estudiadas y complejas entre ellos. Lo que significa que podría ser posible encontrar alguna cadena que haya superado el hash loco sin romper necesariamente los hashes que lo componían.

    
pregunta George Powell 01.04.2013 - 03:24
fuente

10 respuestas

65

El hecho de que necesite hacer esta pregunta es la respuesta en sí misma: no sabe qué tiene de malo apilar estos primitivos, y por lo tanto no puede saber qué beneficios o debilidades hay.

Hagamos un análisis de cada uno de los ejemplos que dio:

md5(md5(salt) + bcrypt(password))

Puedo ver algunos problemas aquí. La primera es que estás MD5'ing la sal. ¿Qué beneficio da esto? Ninguna. Agrega complejidad, y la sal está destinada a ser única para evitar colisiones de contraseñas y ataques de precálculo (por ejemplo, tabla de arco iris). El uso de MD5 aquí no tiene ningún sentido, y en realidad podría debilitar el esquema ya que MD5 tiene colisiones triviales conocidas. Como tal, existe una pequeña posibilidad de que la introducción de MD5 aquí pueda significar que dos sales únicas producen el mismo hash MD5, lo que resulta en una sal efectivamente duplicada. Eso es malo.

A continuación, utiliza bcrypt en la contraseña. De acuerdo. Bueno, la mayoría de las implementaciones de bcrypt requieren un salt internamente, por lo que esto ya no es técnicamente válido. Digamos que lo sabes, y querías decir bcrypt(md5(salt), password) . Esta parte aún está cayendo a la debilidad que describí anteriormente, pero no está tan mal, elimine el MD5 y es un uso estándar de bcrypt.

Finalmente, MD5 todo el asunto. ¿Por qué estás haciendo esto? ¿Cuál es el propósito? ¿Qué beneficio trae? Por lo que puedo ver, no hay ningún beneficio en absoluto. En el lado perjudicial, agrega más complejidad. Dado que la mayoría de las implementaciones de bcrypt utilizan la notación $2a$rounds$salt$hash , tendrá que escribir código para analizar de modo que pueda extraer la parte de hash y almacenar el resto por separado. También necesitarás una implementación MD5, que no fue necesaria.

Por lo tanto, en términos de huella de código para posibles vectores de ataque, ha pasado de una implementación simple de bcrypt a una implementación de bcrypt con código de análisis personalizado e implementación de MD5 y algo de código de pegamento para unirlo todo. Para beneficio cero, y una vulnerabilidad potencial en el manejo de sal.

Siguiente:

scrypt(bcrypt(password + salt))

Este no es tan malo, pero nuevamente necesitas algo de código para analizar los resultados de bcrypt en el hash y el recuento de sal / ronda por separado. En este caso, supongo que supongo que hay un ligero beneficio, porque bcrypt y scrypt funcionan de diferentes maneras para aproximadamente el mismo objetivo, lo que lo haría un poco más difícil para un atacante extremadamente bien financiado para construir ASIC personalizados para romper su esquema. ¿Pero es realmente necesario? ¿De verdad vas a llegar a una situación en la que un estado nación dedicará unos cuantos millones de dólares solo para romper tu hash? Y, si ese caso surge, ¿realmente le molestará al atacante tener que gastar unos cuantos millones extra para duplicar su número de fichas?

Otro problema potencial con la combinación de bcrypt y scrypt de esta manera es que ha habido muy poco estudio sobre cómo interactúan los dos. Como tal, no sabemos si hay algún caso extraño que pueda causar problemas. Como un ejemplo más obvio, toma el pad de una vez. Calculamos c=m^k para un mensaje m y una clave aleatoria k igualmente larga, y obtenemos una seguridad perfecta. Así que hagámoslo dos veces , ¡para una mayor seguridad! Eso nos da c=m^k^k ... oh, espera, eso solo nos da m . Entonces, como no nos tomamos el tiempo para entender correctamente cómo funcionaban las partes internas del sistema, terminamos con una vulnerabilidad de seguridad real. Obviamente, es más complicado en el caso de los KDF, pero se aplica el mismo principio.

Y finalmente:

sha1(md5(scrypt(password + md5(salt))))

Nuevamente nos encontramos con el problema de la sal con MD5. También estoy intrigado por el MD5 con el hash SHA1. ¿Qué posible beneficio podría tener, si ya está utilizando un KDF lento como scrypt? Los pocos nanosegundos que tomaría para calcular esos hash palidecen en comparación con los cientos de milisegundos que tomaría para calcular el resumen de la contraseña de scrypt. Usted está agregando complejidad para una capa de "seguridad" absolutamente irrelevante, lo que siempre es algo malo. Cada línea de código que escriba es una vulnerabilidad potencial.

Ahora recuerda el punto que mencioné al comienzo de mi respuesta. Si, en algún punto de esta respuesta, pensaste "oh sí, no pensé en eso", entonces mi punto está probado.

Te estás encontrando con lo que describiría como Dave La falsa máxima de :

  

Si agrego más criptografía, será más seguro.

Este es un rasgo común entre los desarrolladores, y una vez lo creí también. Va de la mano con la negación de otros principios, como Principio de Kerckhoff . En última instancia, tienes que darte cuenta y aceptar que la oscuridad no es un carril de seguridad; Es una muleta para crypto débil. Si tu criptografía es fuerte, no necesita muleta.

    
respondido por el Polynomial 01.04.2013 - 13:32
fuente
14

Las primitivas criptográficas se pueden apilar de forma segura y aumentan la seguridad si, y solo si, conoces a las primitivas lo suficientemente bien como para comprender sus debilidades y cómo interactúan esas debilidades. Si no los conoce, o no comprende los detalles, bueno, así es como obtiene el protocolo de Dave .

El problema es que muy pocas personas las conocen lo suficientemente bien como para juzgar si una combinación determinada es segura. Es por eso que tiene que ser algo que se publica y revisa, si no se ha revisado, no tiene forma de saber si es tan fuerte como scrypt o si está más cerca de CRC32.

Entonces, si no eres un experto, es muy posible que tengas algo más débil que la primitiva más débil que has usado (consulta el protocolo de Dave) y no lo sabrías. O al menos no lo sabría hasta que está descifrado: encontrar las contraseñas de sus usuarios en Pastebin no es la manera ideal de determinar si el esquema es defectuoso.

Estoy de acuerdo en que un cierto grado de oscuridad puede ayudar desde una perspectiva de defensa en profundidad, pero el sistema subyacente debe ser seguro.

Entre scrypt , bcrypt y PBDKF2 , al menos uno de ellos será compatible en casi todas las plataformas. Estos son conocidos y bien probados: ofrecen diferentes niveles de protección, pero aún son mucho más seguros que un apilamiento extraño de md5 y sha1 .

    
respondido por el Adam Caudill 01.04.2013 - 04:03
fuente
12

Para su pregunta específica de combinar scrypt y bcrypt, recuerde que estas funciones tienen un costo configurable y que desea aumentar ese costo tanto como sea posible, mientras lo mantiene tolerable para su uso específico. Por ejemplo, si puede usar bcrypt con hasta X iteraciones (más allá de las cuales es demasiado costoso para su servidor y su número promedio de conexiones de usuario por segundo), o escríbalo con hasta Y iteraciones, entonces no puede usar scrypt (bcrypt) con X iteraciones para bcrypt luego Y iteraciones para scrypt: esto va más allá su presupuesto de CPU.

Por lo tanto, si coloca en cascada scrypt y bcrypt, entonces debe usar ambos con menos iteraciones de lo que podría haber hecho con uno solo. Usted no "obtiene lo mejor de ambos mundos" simplemente encadenándolos. De hecho, lo mejor que puedes esperar es un tipo de promedio entre los dos. Y eso viene al precio de un código más complejo, algo que es inherentemente malo cuando se habla de seguridad (o, para el caso, mantenimiento).

    
respondido por el Thomas Pornin 01.04.2013 - 19:58
fuente
9

Además de la respuesta de Adam, me gustaría mencionar que cada vez que use la criptografía, debería tener una razón sólida e inevitable para hacerlo. En los ejemplos anteriores, esto no existe.

md5(md5(salt) + bcrypt(password))
scrypt(bcrypt(password + salt))

Los algoritmos bcrypt y scrypt son ya suficientemente fuertes, y se consideran efectivamente irrompibles. ¿Que problema estas tratando de resolver? ¿Y por qué crees que la combinación de sus resultados (especialmente con md5 ) lo resolverá? En el mejor de los casos, es probable que simplemente haya reducido la dificultad de descifrar la contraseña a la del hash más débil, en lugar de mejorar la seguridad. Y el peor de los casos es terriblemente indefinido.

md5(sha1(md5(md5(password) + sha1(password + salt)) + password))

Esta solución es aún peor. Implementa manualmente un esquema de hashing repetido, pero sin suficientes rondas para imponer un factor de trabajo significativo a los atacantes.

En pocas palabras, el problema es que:

  • estás lanzando criptografía sin tener un problema que deba resolverse
  • ha aumentado dramáticamente la probabilidad de introducir fallas en su implementación
  • es probable que haya reducido la seguridad al más débil de los algoritmos de hash, y
  • ha introducido un peor escenario desconocido donde ninguno solía existir
respondido por el Stephen Touset 01.04.2013 - 05:10
fuente
7

Si aplica operaciones no seguras a un algoritmo seguro, definitivamente puede interrumpir la función de hashing. Tu nueva función podría incluso ser mucho peor que el enlace más débil.

¿Por qué los atacantes no usan esto para romper funciones seguras? No les ayuda. Por ejemplo, si sobrescribo los primeros 440 bits de una contraseña almacenada de forma segura utilizando bcrypt con ceros, puedo encontrar fácilmente una contraseña coincidente por fuerza bruta, pero esa contraseña solo funcionará en mi propio algoritmo terrible. Una implementación sana probablemente lo rechazaría.

La reducción a cero de grandes trozos de hash es claramente mala, pero incluso las operaciones seguras se pueden combinar en algo peligroso. Agregar dos números (módulo n para garantizar una longitud constante) es "seguro". En general, no se pierde entropía. Sin embargo, h (x) + h (x) mod n reduce la calidad del hash h (x) en un bit, ya que el resultado es siempre uniforme. El operador igualmente seguro XOR lo hace incluso peor, ya que h (x) XOR h (x) siempre devuelve cero.

Estos escollos son bastante obvios, pero no todos. Tenga en cuenta que, como siempre, es trivial inventar un esquema lo suficientemente bueno como para que no encuentre ninguna debilidad, pero es muy difícil inventar uno donde nadie más pueda hacerlo.

    
respondido por el Marcks Thomas 01.04.2013 - 12:54
fuente
6

Las funciones hash son creadas por criptógrafos y destruidas por criptógrafos. Hay muchas funciones hash fuertes y otras débiles que aún se utilizan hoy en día. Los programadores deben confiar en los criptógrafos y en la función hash. Si alguna vez hubo una vulnerabilidad en la función hash, seguramente lo escucharía en Internet o a través de compañeros de trabajo y luego los criptógrafos seguramente realizarán una investigación profunda. Con cualquier algoritmo hash seguro, solo se sabe que la debilidad puede ser un ataque de fuerza bruta.

La combinación de funciones hash casi no agrega seguridad adicional y cualquier cosa que desee agregar, probablemente ya esté implementada en la función.

La creación de una contraseña es excelente para reducir la efectividad de las tablas de arco iris, por lo que una contraseña no se puede "buscar". Tanto si hash una función dos veces como si cambias la función de hash, es esencialmente la eliminación de la contraseña. Y la mayoría de las funciones incluyen un método fácil de eliminar, por lo que realmente no hay necesidad de implementar esto.

Digamos que quiero crear mi propio hash seguro porque todos lo hacen. Y como no soy un criptógrafo, lo necesitaré "realmente" seguro, porque, por supuesto, cada programador sabe cómo hacer un hash seguro en lugar de utilizar los seguros ya creados. Así que creo mi función hash devious, mod10 (md5 (sha1 (bcrypt (contraseña + sal)))).

Como puedes ver en mi función hash, es realmente segura porque uso muchas cosas diferentes en ella. Por supuesto, en este ejemplo tonto, es fácil ver que solo habrá 10 salidas posibles diferentes. Pero al utilizar una única función de hash segura, habría evitado esto por completo.

  

Claro, su sistema debería estar seguro si el atacante tiene el código fuente, pero es muy probable que su atacante no tenga acceso a su código fuente y probablemente no pueda adivinar su hash loco, haciendo cualquier intento. a una fuerza bruta imposible

Por lo tanto, estamos asumiendo que un atacante se ha hecho con la tabla de la base de datos que contiene los hashes. Supongo que es muy probable que un atacante también pueda obtener los archivos de la página web. Sus sistemas que ejecutan estos servicios pueden ser el mismo exploit que le permitió tomar la base de datos. Se configura un servidor de base de datos para que el público no pueda acceder directamente a él. Por otro lado, su servidor web que contiene su código está en la primera línea.

    
respondido por el ponsfonze 01.04.2013 - 09:36
fuente
5

Cada vez que aumenta la complejidad de un algoritmo o incluso agrega más líneas de código, aumenta los puntos de falla en su aplicación. Puede haber consecuencias no deseadas de combinar algoritmos. Esto puede llevar a ciertos avisos u otros signos que pueden debilitar la fuerza criptográfica del sistema.

Cuantas más bibliotecas utilice en su aplicación, mayor será el riesgo para su aplicación en general. Si se encuentra una falla en una implementación que permite una debilidad, ejecución de código, etc., su aplicación es vulnerable. Si por suerte eligió otro algoritmo que no fue atacado, por el momento está seguro (por supuesto, también podría estar en el lado desafortunado de la suerte).

Recuerda KISS : hazlo simple, estúpido , de lo contrario, puedes perderte. la complejidad.

    
respondido por el Eric G 01.04.2013 - 06:04
fuente
1

Voy a estar en desacuerdo con un grupo de personas que son más inteligentes que yo y con más experiencia en seguridad que yo. Así que probablemente estoy equivocado.

Improvisar tu propia función hash es una buena idea, si lo haces bien. Siga estos 3 pasos simples.

  1. Identifique una debilidad o falla en las funciones hash existentes.
  2. Improvise su propia función hash que no tiene este defecto.
  3. Verifique que su función hash improvisada tenga todas las fortalezas de las funciones hash existentes.

Después de completar el paso 3, sería un tonto no usar la función hash improvisada.

    
respondido por el emory 02.04.2013 - 02:05
fuente
0

Cuando concatene diferentes funciones hash porque le preocupa que sea vital que aplique el salt back antes de aplicar el siguiente algoritmo hash, entonces cualquier debilidad de colisión en uno de los algoritmos no contamina la segunda función hash que está utilizando:

scrypt (bcrypt (contraseña + sal) + sal)

Pero creo que Scrypt es una técnica ya establecida, Argon 2i ha ganado la competencia de hashing de contraseñas y se cree que es más seguro y bcrypt ha existido por mucho tiempo y ha demostrado ser seguro contra los desvíos triviales.

Por lo tanto, lo siguiente tendría más sentido y combinaría la fuerza del argón 2i, pero se reduciría a bcrypt si un futuro atacante mostrara cómo trivialmente romper el argón 2i, que actualmente se cree que es difícil:

bcrypt (Argon2i (contraseña + sal) + sal)

Pero es menos probable que cometa un error si simplemente lo hace:

scrypt (contraseña + sal) O bcrypt (contraseña + sal)

Recuerde, la mayoría de las violaciones se deben a un error humano en el código, mucho mejor para hacerlo simple y revisar minuciosamente su código con revisores dinámicos, de análisis estático y de código humano, para asegurarse de que no se produzcan ataques de inyección de SQL (recuerde que siempre debe parametrizar sus consultas de base de datos!)

    
respondido por el user3083447 13.12.2016 - 15:55
fuente
-2

Aprendí mucho con este hilo, así que pensé de una manera para entender fácilmente el posible resultado cuando se combinan diferentes métodos (por supuesto, no se ajustará a todos los casos):

[W = débil, S = fuerte]:

Formula        | Example
---------------|-----------------
W(W) = W       | md5(sha1(pwd))
W(S) = W       | md5(bcrypt(pwd))
S(W) = W       | bcrypt(md5(pwd)) 
S(S) = S-      | bcrypt(scrypt(pwd)) //their interaction is unknown
W+S = W        | md5(pwd)+brcypt(pwd) 
S+S = W        | bcrypt(pwd)+scrypt(pwd)
W'+S" = S      | md5(pwd1)+brcypt(pwd2) //pwd1 != pwd2
S'+S" = 2S     | bcrypt(pwd1)+scrypt(pwd2) //pwd1 != pwd2
SUBSTR(W) = W  | substr(md5(pwd),n); //Shorter the string, the weaker
SUBSTR(S) = S- | substr(bcrypt(pwd),n); //Shorter the string, the weaker
S+x = S+       | bcrypt(pwd)+x   //x=Variable str. unrelated to pwd
S(S+x) = S-    | bcrypt(scrypt(pwd)+x) //x=Variable str. unrelated to pwd
ROT13(S) = W   | ROT13(bcrypt(pwd))  //If Rot13 has a vulnerability: fail

Lo que quiero lograr aquí es mostrar de manera simple que esas combinaciones no agregarán seguridad adicional en la mayoría de los casos (por lo que la complejidad agregada no es digna).

    
respondido por el lepe 13.04.2016 - 04:06
fuente

Lea otras preguntas en las etiquetas