it-swarm-es.com

¿Por qué los hashes salados son más seguros para el almacenamiento de contraseñas?

Sé que hay muchas discusiones sobre hashes salados, y entiendo que el propósito es hacer que sea imposible construir una tabla Rainbow de todos los hashes posibles (generalmente de hasta 7 caracteres).

Tengo entendido que los valores salados aleatorios simplemente se concatenan con el hash de contraseña. ¿Por qué no se puede utilizar una tabla Rainbow contra el hash de contraseña e ignorar los primeros X bits que se sabe que son hash de sal aleatorios?

Actualización

Gracias por las respuestas Supongo que esto funciona, el directorio (LDAP, etc.) tiene que almacenar una sal específica para cada usuario, o parece que la sal estaría "perdida" y la autenticación nunca podría ocurrir.

250
Tsyras

Normalmente funciona así:

Digamos que su contraseña es "baseball". Simplemente podría almacenarlo sin procesar, pero cualquiera que obtenga mi base de datos obtiene la contraseña. Entonces, en cambio, hago un hash SHA1 y obtengo esto:

$ echo -n baseball | sha1sum
a2c901c8c6dea98958c219f6f2d038c44dc5d362

Teóricamente es imposible revertir un hash SHA1. Pero vaya a hacer na búsqueda en google en esa cadena exacta , y no tendrá problemas para recuperar la contraseña original.

Además, si dos usuarios en la base de datos tienen la misma contraseña, tendrán el mismo hash SHA1. Y si uno de ellos tiene un sugerencia de contraseña que dice try "baseball" - bueno ahora sé cuáles son ambas contraseñas de los usuarios.

Entonces, antes de hacer el hash, anteponemos una cadena única. No es un secreto , solo algo único. Qué tal si WquZ012C. Así que ahora estamos troceando la cadena WquZ012Cbaseball. Eso se apresura a esto:

c5e635ec235a51e89f6ed7d4857afe58663d54f5

Buscar en Google esa cadena no muestra nada (excepto quizás esta página), así que ahora estamos en algo. Y si person2 también usa "baseball" como su contraseña, usamos una sal diferente y obtenemos un hash diferente.

Por supuesto, para probar su contraseña, debe saber cuál es la sal. Entonces tenemos que almacenar eso en algún lugar. La mayoría de las implementaciones simplemente lo agregan directamente con el hash, generalmente con algún delimitador. Pruebe esto si tiene openssl instalado:

[tylerl ~]$ openssl passwd -1
Password: baseball
Verifying - Password: baseball
$1$oaagVya9$NMvf1IyubxEYvrZTRSLgk0

Esto nos da un hash usando la biblioteca estándar crypt. Entonces nuestro hash es $1$oaagVya9$NMvf1IyubxEYvrZTRSLgk0: en realidad son 3 secciones separadas por $. Reemplazaré el delimitador con un espacio para hacerlo más visualmente claro:

$1$oaagVya9$NMvf1IyubxEYvrZTRSLgk0
 1 oaagVya9 NMvf1IyubxEYvrZTRSLgk0
  • 1 significa "algoritmo número 1" que es un poco complicado , pero usa MD5. Hay muchos otros que son mucho mejores , pero este es nuestro ejemplo.
  • oaagVya9 es nuestra sal. Nos hundimos allí con nuestro hash.
  • NMvf1IyubxEYvrZTRSLgk0 es la suma real de MD5, codificada en base64.

Si ejecuto el proceso nuevamente, obtengo un hash completamente diferente con una sal diferente. En este ejemplo, hay alrededor de 1014 formas de almacenar esta contraseña. Todos estos son para la contraseña "baseball":

$1$9XsNo9.P$kTPuyvrHqsJJuCci3zLwL.
$1$nLEOCtx6$uSnz6PF8q3YuUhB3rLTC3/
$1$/jZJXTF3$OqDuk8T/cEIGpeKWfsamf.
$1$2lC.Cb/U$KR0jkhpeb1sz.UIqvfYOR.

Pero, si especifico deliberadamente la sal que quiero verificar, obtendré el resultado esperado:

[tylerl ~]$ openssl passwd -1 -salt oaagVya9
Password: baseball
Verifying - Password: baseball
$1$oaagVya9$NMvf1IyubxEYvrZTRSLgk0

Y esa es la prueba que ejecuto para verificar si la contraseña es correcta. Encuentre el hash almacenado para el usuario, encuentre la sal guardada, vuelva a ejecutar ese mismo hash usando sal guardada, verifique si el resultado coincide con el hash original.

Implementando esto usted mismo

Para ser claros, esta publicación no es una guía de implementación. No se limite a salar su MD5 y dígale que es bueno. Eso no es suficiente en el clima de riesgo actual. En su lugar, querrá ejecutar un proceso iterativo que ejecute la función hash miles de veces. Esto ha sido explicado en otra parte muchas veces, así que no voy a repasar el "por qué" aquí.

Hay varias opciones bien establecidas y confiables para hacer esto:

  • crypt: La función que utilicé anteriormente es una variación anterior en el mecanismo de hash de contraseña crypt de Unix incorporado- en todos los sistemas operativos Unix/Linux. La versión original (basada en DES) es terriblemente insegura; Ni siquiera lo consideres. El que mostré (basado en MD5) es mejor, pero aún no debería usarse hoy. Las variaciones posteriores, incluidas las variaciones SHA-256 y SHA-512, deberían ser razonables. Todas las variantes recientes implementan múltiples rondas de hashes.

  • bcrypt: La versión blowfish de la llamada funcional crypt mencionada anteriormente. Aprovecha el hecho de que el pez globo tiene un proceso de configuración de claves muy costoso y toma un parámetro de "costo" que aumenta el tiempo de configuración de la clave en consecuencia.

  • PBKDF2: ("Función de derivación de clave basada en contraseña versión 2") Creado para producir claves criptográficas seguras a partir de contraseñas simples, esto es la única función listada aquí que en realidad tiene un RFC . Ejecuta un número configurable de rondas, con cada ronda contiene la contraseña más el resultado de la ronda anterior. La primera ronda usa una sal. Vale la pena señalar que su propósito original es crear claves fuertes , no almacenar contraseñas , pero la superposición de objetivos hace que esto sea un buen de confianza aquí también. Si no tenía bibliotecas disponibles y se vio obligado a implementar algo desde cero, esta es la opción más fácil y mejor documentada. Aunque, obviamente, usar una biblioteca bien investigada siempre es lo mejor.

  • scrypt: Un sistema introducido recientemente diseñado específicamente para ser difícil de implementar en hardware dedicado. Además de requerir múltiples rondas de una función de hashing, scrypt también tiene un estado de memoria de trabajo muy grande, para aumentar el requisito RAM para implementaciones.Aunque es muy nuevo y en su mayoría no probado, parece al menos tan seguro como los demás, y posiblemente el más seguro de todos.

402
tylerl

Técnicamente, todavía puedes usar una mesa Rainbow para atacar hashes salados. Pero solo técnicamente. Un hash salado derrota los ataques de la mesa Rainbow, no agregando magia criptográfica, sino simplemente aumentando exponencialmente el tamaño de la mesa Rainbow requerida para encontrar con éxito una colisión.

Y sí, necesitarás almacenar la sal :)

49
xkcd

No se agrega después del hash. Se agrega antes del hash, por lo que el hash es completamente diferente para cada sal.

No es

hash abcd = defg
store 123defg (for user with 123 salt) and 456defg (for user with 456 salt)  

Está

hash 123abcd = ghij 
hash 456abcd = klmn
23
AJ Henderson

Para los hashes de contraseñas, debe usar algo como PBKDF2/RFC2898/PKCS5v2 , Bcrypt o Scrypt, todo lo cual le permite seleccionar una cantidad de iteraciones ("factor de trabajo"), en lugar de solo una . PBKDF2, por ejemplo, usa HMAC hashing codificado con un algoritmo hash bien conocido (típicamente SHA-512, SHA-256 o SHA-1) internamente para número de iteraciones, preferiblemente decenas a cientos de miles tan alto como pueda mantenerlo sin que los usuarios se quejen, esencialmente.

La razón de la gran cantidad de iteraciones es ralentizar a un atacante, lo que a su vez reduce el espacio de teclas que pueden atravesar en un período de tiempo determinado, por lo que no pueden atacar las mejores contraseñas de manera efectiva. "contraseña" y "P @ $$ w0rd" se descifrarán independientemente de un ataque fuera de línea, obviamente.

Tiene razón, cada fila (usuario) necesita su propia sal larga criptográficamente aleatoria generada de forma única. Esa sal se almacena en forma de texto sin formato.

Con PBKDF2, Bcrypt o Scrypt, también recomendaría almacenar el número de iteraciones (factor de trabajo) en la base de datos también en texto sin formato, por lo que es fácil de cambiar (personalmente, uso un número de iteraciones al azar, si siempre es diferente , entonces hay menos temor sobre "oh no, un pequeño cambio podría estropearlo todo - NUNCA CAMBIAR ESTO DE NUEVO" de la gerencia u otros desarrolladores)

Tenga en cuenta que cuando use PBKDF2 para el hash de contraseña, nunca solicite una salida mayor que la salida de hash nativa: para SHA-1, eso es 20 bytes, y para SHA-512, eso es 64 bytes.

Para dar ejemplos reales de PBKDF2 con dos sales diferentes:

(PBKDF2 HMAC contraseña sal iteraciones outputbytes resultados)

  • PBKDF2 HMAC-SHA-512 MyPass vA8u3v4qzCdb 131072 64
    • 2e3259bece6992f012966cbf5803103fdea7957ac20f3ec305d62994a3f4f088f26cc3889053fb59a4e3c282f55e9179695609ee1147cffae1455880993ef874
  • PBKDF2 HMAC-SHA-512 MyPass l6eZQVf7J65S 131072 64
    • 1018ad648096f7814bc2786972eb4091f6c36761a8262183c24b0f4d34abb48073ed2541ee273220915638b46ec14dfb2b23ad64c4aa12f97158340bdc12fc57
15
Anti-weakpasswords

Además de lo que dijo tylerl, es importante enfatizar que en la criptografía moderna, las sales no se usan para proteger contra las tablas Rainbow. Nadie realmente usa las mesas Rainbow. El verdadero peligro es la paralelización:

  • Si no tienes sal, o solo una estática sal, y te robo tu DB de un millón de hashes, puedo arrojar todos mis núcleos a fuerza bruta, y cada núcleo atacará un millón de hashes simultáneamente, porque cada vez que presione cualquiera de las cadenas utilizadas como contraseñas, veré una coincidencia de hash. Así que necesito agotar el espacio de búsqueda na vez para descifrar las contraseñas millones. Esa es una escala completamente realista de pérdida de contraseña, y un objetivo lo suficientemente atractivo como para lanzar un par de docenas de GPU o incluso un FPGA personalizado. Incluso si solo agoto el 30% inferior del espacio de búsqueda, todavía me iré con 500k contraseñas del mundo real más o menos. Esas contraseñas se usarán para construir mi diccionario, por lo que la próxima vez que alguien filtre un hash DB, descifraré el 90% de ellas en horas.
  • Si, en cambio, todas y cada una de las contraseñas se combinan con su propia sal única, entonces tendré que atacar a cada una de ellas individualmente, porque incluso las contraseñas idénticas tendrán almacenadas hashes completamente diferentes. Eso significa que tal vez podría usar mi granja GPU/FPGA costosa y hambrienta de energía para atacar a los dos objetivos de alto valor (por ejemplo, si Obama era su usuario, entonces obtener su contraseña aún garantizaría el gasto), pero no obtendré varios cientos de miles de contraseñas gratis de eso. Si quisiera obtener las contraseñas completas millones, tendría que hacer una búsqueda completa de fuerza bruta a millones veces.

Y es por eso que cualquier sal, estática o no, lo protegerá contra las tablas preparadas de Rainbow siempre y cuando no se use ampliamente, pero solo las sales únicas por hash lo protegerán contra el peligro real de usar mucha potencia de computación paralela para descifrar todo En seguida.

14
mathrick

Es más seguro porque cuando varias personas tienen la misma contraseña, tendrán un hash diferente.

Implementación simple usando Python:

import hashlib

passwordA = '123456'
passwordB = '123456'

hashlib.md5(passwordA).hexdigest()
'e10adc3949ba59abbe56e057f20f883e'

hashlib.md5(passwordB).hexdigest()
'e10adc3949ba59abbe56e057f20f883e'

Y ahora agreguemos sal:

saltA = 'qwerty'
salbB = 'asdfgh'

passA = passwordA + saltA
passB = passwordB + saltB

hashlib.md5(passA).hexdigest()
'086e1b7e1c12ba37cd473670b3a15214'

hashlib.md5(passB).hexdigest()
'dc14768ac9876b3795cbf52c846e6847'

Esta es una versión muy simplificada. Puedes agregar sal donde prefieras. Al principio/medio/final de la contraseña.

Salt es muy útil, especialmente cuando tienes muchos usuarios, por lo que cada uno de ellos tendrá diferentes hashes. Ahora imagine la probabilidad de tener las mismas contraseñas para millones de cuentas, como Facebook, Google o Twitter.

11
zakiakhmad

Primero, si dos usuarios usan la contraseña "baseball" más bien débil, entonces descifrar una contraseña no ayuda a descifrar la segunda contraseña debido a la sal. Sin sal, has descifrado dos contraseñas por el precio de una.

En segundo lugar, las tablas Rainbow contienen hashes precalculados. Por lo tanto, una galleta podría buscar el hash de "béisbol" en una mesa Rainbow con un billón de entradas. Mucho más rápido que calcular los miles de millones de hashes en la mesa Rainbow. Y eso se evita con la salazón.

Y ahora uno importante: algunas personas tienen buenas contraseñas, otras tienen malas contraseñas. Si tiene un millón de usuarios, y tres usan la contraseña idéntica, solo saber esa contraseña es débil. Sin sal, tienes tres hashes idénticos. Entonces, si alguien roba su base de datos de hashes, puede ver de inmediato qué usuarios tienen contraseñas débiles y concentrarse en descifrarlos. Mucho más fácil de descifrar "béisbol" que 1keOj29fa0romn. Sin sal, se destacan las contraseñas débiles. Con el salado, el cracker no tiene idea de qué contraseñas son débiles y cuáles son difíciles de descifrar.

8
gnasher729

Si el hash es salado o no solo hace una diferencia si el atacante tiene el hash de contraseña. Sin sal, el hash se puede atacar con una tabla Rainbow: un diccionario precalculado que asocia las contraseñas con los hash. Una sal de N bits aumenta el requisito de almacenamiento para una tabla Rainbow y el tiempo para calcular esa tabla en un factor de 2 ** N. Entonces, por ejemplo, con una sal de 32 bits, solo una entrada de diccionario de tabla Rainbow, digamos para la contraseña passw0rd, requiere muchos gigabytes de almacenamiento, lo que hace que la tabla Rainbow sea muy costosa con el hardware de almacenamiento actual. Entonces, cuando hay una sal, el atacante se reduce a un ataque de fuerza bruta en el conjunto específico de hashes de contraseñas que se han obtenido.

Sin embargo:

  • para contraseñas débiles, un ataque de fuerza bruta tendrá éxito en relativamente poco tiempo.
  • no se encontrarán contraseñas suficientemente seguras en una tabla Rainbow.
  • si el atacante tiene acceso a los hashes, la seguridad del sistema ya se ha visto comprometida: los sistemas y protocolos modernos no revelan sus hashes de contraseñas. Si el atacante no puede obtener acceso a la base de datos de contraseñas, las contraseñas también pueden almacenarse en texto sin formato.
  • si un atacante debe comprometer el sistema para llegar a los hash para revertir sus contraseñas, entonces las únicas contraseñas que tienen valor para el atacante son aquellas que se reutilizan para otros dominios de seguridad a los que el atacante aún no tiene acceso. Las contraseñas que no se reutilizan no tienen valor (y ciertamente menos valor que otra información confidencial asociada con las cuentas).

Digamos que el usuario joewestlake usa la contraseña god1234. El atacante invierte esto instantáneamente usando una mesa Rainbow. (O a los pocos minutos de descifrar usando un ataque de fuerza bruta, si el hash está salado, ya que la contraseña es muy mala.) Ahora el problema es que joewestlake también usó god1234 para su cuenta de Gmail y para la banca en línea, ¡Uy! Ahora el atacante lee los correos electrónicos de Joe y aprende lo suficiente sobre Joe para que pueda responder fácilmente a la pregunta "cuál era el nombre de su primera mascota" al iniciar sesión en la banca en línea de Joe.

Por lo tanto, la razón de ser de las sales es que protegen a los usuarios un poco haciendo que las contraseñas de seguridad media sean más difíciles de revertir: contraseñas que, sin una sal, podrían razonablemente ser se espera que se encuentren en una mesa Rainbow, pero que son lo suficientemente fuertes como para que forzarlos de forma bruta individualmente lleve mucho tiempo. Pero las sales proporcionan este beneficio solo en el caso de que los hash se vean comprometidos, lo que ya es una violación de seguridad grave, y el beneficio es solo para aquellos usuarios que reutilizan sus contraseñas de seguridad media en otros sistemas.

Digamos que Joe usó una contraseña compuesta de 10 caracteres alfanuméricos aleatorios y símbolos. Esto aún podría estar en una mesa Rainbow, pero requiere mucho trabajo. Entonces, incluso si Joe usó la misma contraseña para Gmail y la banca en línea, está seguro, gracias a la sal. El cracker ejecuta su crack de fuerza bruta durante quizás varias horas, quizás días. El crack produce numerosas contraseñas débiles de otros usuarios del mismo sistema que tienen contraseñas débiles. El atacante está satisfecho con ese rendimiento y deja de agrietarse; La contraseña de Joe nunca se revierte.

Además, si se detecta la violación y se aconseja a los usuarios (incluido Joe) que cambien sus contraseñas, entonces Joe tiene la oportunidad de superar los intentos de descifrado del atacante, incluso si el atacante persiste en atacar todo el espacio de contraseña de seguridad media que incluye la contraseña de Joe . Joe lo sabe, la contraseña que usó en el sistema comprometido es exactamente la misma que su contraseña de Gmail y de su banco, por lo que se apresura a cambiar las otras dos. La sal ayuda aquí porque les da tiempo a los usuarios que reutilizan contraseñas para que cambien su contraseña. La sal no ayudará a aquellos con contraseñas muy débiles que se descifran en minutos, pero los usuarios de contraseñas que tardan horas o días en descifrar tienen una oportunidad de luchar.

1
Kaz