Mejores prácticas: ¿Contraseñas de sal y pimienta?

145

Encontré una discusión en la que aprendí que lo que había estado haciendo no era, de hecho, salar las contraseñas, sino sazonarlas, y desde entonces he comenzado a hacer ambas cosas con una función como:

hash_function($salt.hash_function($pepper.$password)) [multiple iterations]

Ignorando el algoritmo hash elegido (quiero que esto sea una discusión sobre sales y pimientos y no algoritmos específicos, pero estoy usando uno seguro), ¿es esta una opción segura o debería hacer algo diferente? Para aquellos que no están familiarizados con los términos:

  • Una sal es un valor generado aleatoriamente que generalmente se almacena con la cadena en la base de datos diseñada para que sea imposible usar tablas hash para descifrar contraseñas. Como cada contraseña tiene su propia sal, todas deben ser forzadas individualmente para poder descifrarlas; sin embargo, como la sal se almacena en la base de datos con el hash de contraseña, un compromiso de la base de datos significa perder ambos.

  • Un pimiento es un valor estático de todo el sitio almacenado por separado de la base de datos (generalmente codificado en el código fuente de la aplicación) que pretende ser secreto. Se utiliza para que un compromiso de la base de datos no haga que la tabla de contraseñas de toda la aplicación sea bruta.

¿Me falta algo? ¿Salar y salpimentar mis contraseñas es la mejor opción para proteger la seguridad de mi usuario? ¿Hay alguna falla de seguridad potencial para hacerlo de esta manera?

Nota: Suponga a los fines de la discusión que la aplicación y la base de datos se almacenan en máquinas separadas, no comparten contraseñas, etc., por lo que una violación del servidor de la base de datos no significa automáticamente una violación del servidor de la aplicación.

Glitch Desire
fuente
3
No es un duplicado, pero está extremadamente relacionado: stackoverflow.com/questions/16594613/…
ircmaxell
2
Duplicado entre sitios: security.stackexchange.com/q/3272/2113
Jacco

Respuestas:

306

Okay. Ya que necesito escribir sobre esto una y otra vez , haré una última respuesta canónica solo con pimienta.

El aparente revés de los pimientos

Parece bastante obvio que los pimientos deberían hacer que las funciones hash sean más seguras. Quiero decir, si el atacante solo obtiene tu base de datos, las contraseñas de tus usuarios deberían ser seguras, ¿verdad? Parece lógico, ¿verdad?

Es por eso que tanta gente cree que los pimientos son una buena idea. Que tiene sentido".

La realidad de los pimientos

En los reinos de seguridad y criptografía, "tener sentido" no es suficiente. Algo tiene que ser demostrable y tener sentido para que se considere seguro. Además, tiene que ser implementable de manera sostenible. El sistema más seguro que no se puede mantener se considera inseguro (porque si alguna parte de esa seguridad se rompe, todo el sistema se desmorona).

Y los pimientos no se ajustan ni a los modelos comprobables ni a los que se pueden mantener ...

Problemas teóricos con pimientos

Ahora que hemos preparado el escenario, veamos qué hay de malo con los pimientos.

  • Alimentar un hash en otro puede ser peligroso.

    En tu ejemplo, lo haces hash_function($salt . hash_function($pepper . $password)).

    Sabemos por experiencia pasada que "simplemente alimentar" un resultado de hash en otra función de hash puede disminuir la seguridad general. La razón es que ambas funciones hash pueden convertirse en un objetivo de ataque.

    Es por eso que algoritmos como PBKDF2 utilizan operaciones especiales para combinarlos (hmac en ese caso).

    El punto es que si bien no es un gran problema, tampoco es algo trivial simplemente tirar. Los sistemas de cifrado están diseñados para evitar casos de "debería funcionar", y en su lugar se centran en casos de "diseñados para funcionar".

    Si bien esto puede parecer puramente teórico, de hecho no lo es. Por ejemplo, Bcrypt no puede aceptar contraseñas arbitrarias . Por lo tanto, pasar bcrypt(hash(pw), salt)puede resultar en un hash mucho más débil que bcrypt(pw, salt)si hash()devuelve una cadena binaria.

  • Trabajando contra el diseño

    La forma en que se diseñó bcrypt (y otros algoritmos de hashing de contraseñas) es trabajar con sal. El concepto de pimiento nunca fue introducido. Esto puede parecer una trivialidad, pero no lo es. La razón es que una sal no es un secreto. Es solo un valor que un atacante puede conocer. Un pimiento por otro lado, por definición es un secreto criptográfico.

    Los algoritmos de hashing de contraseña actuales (bcrypt, pbkdf2, etc.) están diseñados para tomar solo un valor secreto (la contraseña). Agregar otro secreto al algoritmo no se ha estudiado en absoluto.

    Eso no significa que no sea seguro. Significa que no sabemos si es seguro. Y la recomendación general con seguridad y criptografía es que si no lo sabemos, no lo es.

    Entonces, hasta que los criptógrafos diseñen y examinen los algoritmos para usarlos con valores secretos (pimientos), los algoritmos actuales no deben usarse con ellos.

  • La complejidad es el enemigo de la seguridad

    Lo creas o no, la complejidad es el enemigo de la seguridad . Hacer un algoritmo que parezca complejo puede ser seguro o no. Pero las posibilidades son bastante significativas de que no sea seguro.

Problemas significativos con pimientos

  • No es mantenible

    Su implementación de pimientos impide la capacidad de girar la llave de pimiento. Como el pimiento se usa en la entrada de la función unidireccional, nunca se puede cambiar el pimiento durante la vida útil del valor. Esto significa que necesitarías crear algunos trucos inestables para que sea compatible con la rotación de teclas.

    Esto es extremadamente importante ya que se requiere siempre que almacene secretos criptográficos. No tener un mecanismo para rotar las llaves (periódicamente y después de una violación) es una gran vulnerabilidad de seguridad.

    Y su enfoque actual de Pepper requeriría que cada usuario tenga su contraseña completamente invalidada por una rotación, o espere hasta su próximo inicio de sesión para rotar (que puede que nunca sea ...)

    Lo que básicamente hace que su enfoque sea inmediato.

  • Requiere que ruedes tu propio cripto

    Dado que ningún algoritmo actual admite el concepto de un pimiento, requiere componer algoritmos o inventar otros nuevos para admitir un pimiento. Y si no puedes ver de inmediato por qué es algo realmente malo:

    Cualquiera, desde el aficionado más despistado hasta el mejor criptógrafo, puede crear un algoritmo que él mismo no puede descifrar.

    NUNCA haga rodar su propia cripto ...

La mejor manera

Entonces, de todos los problemas detallados anteriormente, hay dos formas de manejar la situación.

  • Simplemente use los algoritmos tal como existen

    Si usa bcrypt o scrypt correctamente (con un alto costo), todas las contraseñas de diccionario menos las más débiles deberían ser estadísticamente seguras. El récord actual de hash bcrypt al costo 5 es de 71k hashes por segundo. A ese ritmo, incluso una contraseña aleatoria de 6 caracteres tardaría años en descifrarse. Y teniendo en cuenta que mi costo mínimo recomendado es 10, eso reduce los hashes por segundo en un factor de 32. Por lo tanto, estaríamos hablando solo de 2200 hashes por segundo. A ese ritmo, incluso algunas frases de diccionario o modificaciones pueden ser seguras.

    Además, deberíamos verificar esas clases débiles de contraseñas en la puerta y no permitir que entren. A medida que el descifrado de contraseñas se hace más avanzado, también deberían hacerlo los requisitos de calidad de las contraseñas. Sigue siendo un juego estadístico, pero con una técnica de almacenamiento adecuada y contraseñas seguras, todos deberían ser prácticamente muy seguros ...

  • Cifre el hash de salida antes del almacenamiento

    Existe en el ámbito de la seguridad un algoritmo diseñado para manejar todo lo que hemos dicho anteriormente. Es un cifrado en bloque. Es bueno, porque es reversible, por lo que podemos rotar las teclas (¡yay! ¡Mantenibilidad!). Es bueno porque se está utilizando según lo diseñado. Es bueno porque no le da al usuario información.

    Miremos esa línea nuevamente. Digamos que un atacante conoce su algoritmo (que es necesario para la seguridad, de lo contrario es seguridad a través de la oscuridad). Con un enfoque tradicional de pimienta, el atacante puede crear una contraseña de centinela, y dado que conoce la sal y la salida, puede forzar la pimienta con fuerza bruta. Ok, es una posibilidad remota, pero es posible. Con un cifrado, el atacante no obtiene nada. Y dado que la sal es aleatoria, una contraseña centinela ni siquiera lo ayudará. Entonces, lo mejor que les queda es atacar la forma encriptada. Lo que significa que primero tienen que atacar su hash cifrado para recuperar la clave de cifrado y luego atacar los hash. Pero hay mucha investigación sobre el ataque de las cifras, por lo que queremos confiar en eso.

TL / DR

No uses pimientos. Hay una serie de problemas con ellos, y hay dos formas mejores: no usar ningún secreto del lado del servidor (sí, está bien) y cifrar el hash de salida usando un cifrado de bloque antes del almacenamiento.

ircmaxell
fuente
66
Gracias por incluir esa última parte del cifrado del valor hash, esta es una respuesta con la que estoy totalmente de acuerdo. Si el cifrado se convertiría en parte de su API contraseña , no habría ninguna razón para no usarlo, así que tal vez ... (i encantaría escribir la documentación para ello)
martinstoeckli
2
@martinstoeckli: no estaría de acuerdo en agregar el paso de cifrado a la API de hashing simplificada. La razón es que el almacenamiento de secretos (claves) es mucho más difícil de lo que la gente cree, y es bastante fácil dispararse en el pie. Para el 99.9% de los usuarios, bcrypt sin procesar es más que suficiente para todas las contraseñas, excepto las más simples ...
ircmaxell
77
@ircmaxell - Por otro lado, no perderías nada. En el peor de los casos, cuando se conoce la clave, un atacante aún debe descifrar el hash de BCrypt (misma situación que sin cifrado). Esto no es lo mismo que almacenar una clave para cifrar datos, se trata de agregar un secreto del lado del servidor. Incluso una clave codificada protegería esas contraseñas débiles, siempre que el atacante no tenga control sobre el servidor / código. Esta situación no es infrecuente: aparte de la inyección SQL, también se descartan las copias de seguridad, los servidores descartados ... pueden conducir a esta situación. Muchos usuarios de PHP trabajan en servidores alojados.
martinstoeckli
2
@martinstoeckli Tendría que estar de acuerdo con ircmaxwell aquí, no creo que el cifrado pertenezca a la API de contraseña. Sin embargo, podría notarse que para la máxima seguridad, el cifrado de los hash es una disposición adicional que se puede utilizar.
foochow
1
Me señalaron a OWASP Application Security Verification Standard 4.0 sección 2.4.5 que recomienda usar sal secreta (también conocida como pepper): “Verifique que se realice una iteración adicional de una función de derivación de clave, usando un valor de sal que es secreto y que solo conoce el verificador ".
Michael Freidgeim
24

Primero, deberíamos hablar sobre la ventaja exacta de un pimiento :

  • El pepper puede proteger las contraseñas débiles de un ataque de diccionario, en el caso especial, donde el atacante tiene acceso de lectura a la base de datos (que contiene los hashes) pero no tiene acceso al código fuente con el pepper.

Un escenario típico sería la inyección de SQL, las copias de seguridad desechadas, los servidores descartados ... Estas situaciones no son tan infrecuentes como parece y, a menudo, no están bajo su control (servidor de alojamiento). Si utiliza...

  • Una sal única por contraseña
  • Un algoritmo de hashing lento como BCrypt

... las contraseñas seguras están bien protegidas. Es casi imposible forzar una contraseña segura en esas condiciones, incluso cuando se conoce la sal. El problema son las contraseñas débiles, que forman parte de un diccionario de fuerza bruta o son derivaciones de ellas. Un ataque de diccionario revelará esos muy rápido, porque prueba solo las contraseñas más comunes.

La segunda pregunta es cómo aplicar la pimienta .

Una forma a menudo recomendada de aplicar una pimienta es combinar la contraseña y la pimienta antes de pasarla a la función hash:

$pepperedPassword = hash_hmac('sha512', $password, $pepper);
$passwordHash = bcrypt($pepperedPassword);

Sin embargo, hay otra forma aún mejor:

$passwordHash = bcrypt($password);
$encryptedHash = encrypt($passwordHash, $serverSideKey);

Esto no solo permite agregar un secreto del lado del servidor, sino que también permite intercambiar $ serverSideKey, en caso de que sea necesario. Este método implica un poco más de trabajo, pero si el código alguna vez existe (biblioteca) no hay razón para no usarlo.

martinstoeckli
fuente
¿Entonces diría que un pimiento agrega seguridad sobre solo una sal, en resumen? Gracias por la ayuda sobre cómo implementar.
Glitch Desire
1
@LightningDust: sí, para contraseñas débiles, siempre y cuando el pimiento permanezca en secreto. Mitiga algunos tipos bien definidos de amenazas.
martinstoeckli
@martinstoeckli definitivamente es una buena forma de implementar esto. Es bueno ver que alguien con cierta experiencia en seguridad es compatible con este método. ¿Una AES_ENCRYPT($passwordHash, $serverSideKey)llamada de MySQL también sería una forma apropiada de implementar esto?
foochow
1
@Foo_Chow: no conozco la implementación de la función MySQL, pero parece que utilizaron el modo EBC para evitar el vector IV. Junto con el texto sin formato conocido (los valores hash siempre comienzan con los mismos caracteres), esto podría ser un problema. En mi página de inicio publiqué una implementación de ejemplo, que maneja este cifrado.
martinstoeckli
@martinstoeckli interesante, no demasiado familiarizado con los conceptos; Sin embargo, parece que un IV sería deseable para obtener los resultados más sólidos. No parece agregar mucha sobrecarga para el beneficio adicional.
foochow
2

El objetivo de la sal y la pimienta es aumentar el costo de una búsqueda de contraseña precalculada, llamada tabla de arcoiris.

En general, tratar de encontrar una colisión para un solo hash es difícil (suponiendo que el hash sea seguro). Sin embargo, con hashes cortos, es posible usar la computadora para generar todos los hashes posibles en una búsqueda en un disco duro. Esto se llama una mesa arcoiris. Si crea una tabla de arcoíris, puede salir al mundo y encontrar rápidamente contraseñas plausibles para cualquier hash (sin sal y sin sal).

El objetivo de un pimiento es hacer que la tabla del arco iris sea necesaria para hackear su lista de contraseñas única. Perder así más tiempo en el atacante para construir la mesa del arco iris.

Sin embargo, el objetivo de la sal es hacer que la tabla de arco iris para cada usuario sea única para el usuario, aumentando aún más la complejidad del ataque.

Realmente, el objetivo de la seguridad informática es casi nunca hacerlo (matemáticamente) imposible, solo matemáticamente y físicamente poco práctico (por ejemplo, en sistemas seguros se necesitaría toda la entropía en el universo (y más) para calcular la contraseña de un solo usuario).

Aron
fuente
Entonces, ¿una sal + pimienta ofrece más seguridad que solo una sal? ¿O sería mejor dejar caer la pimienta y ejecutar más iteraciones de scrypt?
Glitch Desire
2
Lo principal de una tabla de arcoiris es que no creas una para un ataque específico, sino que descargas una existente. ¡Hay largas tablas de arco iris disponibles para algoritmos hash populares a solo un google de distancia!
Richard
1
@ LightningDust No puedo pensar en ninguna razón por mí mismo. Sin embargo, Richard en el otro hilo se le ocurrió uno. Puede ocultar el pimiento en su código fuente, lo que significa otro lugar al que su atacante necesita acceder, solo para obtener una mesa de arcoíris.
Aron
@Aron: Bueno, eso es lo que pensé, ya que tenemos servidores de aplicaciones separados de los servidores de bases de datos (es decir, si rootaccedes a nuestro servidor de base de datos , todavía no tienes acceso a nuestro servidor de aplicaciones), eso oculta pimienta en el código fuente (en nuestro archivo de configuración) proporcionaría seguridad adicional.
Glitch Desire
1

No puedo ver el almacenamiento de un valor codificado en su código fuente como relevante para la seguridad. Es seguridad a través de la oscuridad.

Si un hacker adquiere su base de datos, podrá comenzar a forzar de forma bruta sus contraseñas de usuario. El pirata informático no tardará mucho en identificar su pimiento si logra descifrar algunas contraseñas.

Sven
fuente
1
Inutilizaría una tabla de arco iris precalculada para una tabla de contraseña sin sal. En resumen, incluso si el atacante conociera tu pimienta, necesitaría crear una nueva tabla de arcoíris.
Aron
3
Fortalece el hash basado en datos no disponibles en la base de datos, lo cual es algo bueno. Las cosas en la base de datos pueden revelarse potencialmente en vulnerabilidades: es menos probable que se acceda a un valor en el código de la misma manera.
Richard
Las sales son funciones unidireccionales, incluso forzar con éxito una contraseña con fuerza bruta solo le daría esa contraseña y no lo ayudaría a obtener el valor de la pimienta en sí.
Glitch Desire
1
@LightningDust Creo que te refieres a "Los hashes son funciones de trampillas". Sí, es imposible descubrir su sal y / o pimienta a partir de un hash seguro (de hecho, esa es la definición de un hash seguro).
Aron
66
@Aron Security Through Obscurity puede ser una técnica válida utilizada como capa de defensa. El punto es que nunca se debe confiar en ella como defensa, sino que se debe usar para "ralentizar a un atacante". Si ese no fuera el caso, no se usaría algo así como un honeypot. En cambio, podemos hacer uso efectivo de la seguridad a través de la oscuridad para ayudar a ralentizar a los atacantes, siempre y cuando no dependamos de ella para la seguridad de nuestra aplicación.
ircmaxell