¿Cómo puede bcrypt tener sales incorporadas?

617

El artículo de Coda Hale "Cómo almacenar una contraseña de forma segura" afirma que:

bcrypt tiene sales incorporadas para prevenir ataques de mesa arcoiris.

Cita este documento , que dice que en la implementación de OpenBSD de bcrypt:

OpenBSD genera la sal bcrypt de 128 bits a partir de una secuencia de claves arcfour (arc4random (3)), sembrada con datos aleatorios que el núcleo recopila de los tiempos del dispositivo.

No entiendo cómo puede funcionar esto. En mi concepción de una sal:

  • Debe ser diferente para cada contraseña almacenada, de modo que se deba generar una tabla de arcoíris separada para cada
  • Debe almacenarse en algún lugar para que sea repetible: cuando un usuario intenta iniciar sesión, tomamos su intento de contraseña, repetimos el mismo procedimiento de sal y hash que hicimos cuando originalmente almacenamos su contraseña y comparamos

Cuando uso Devise (un administrador de inicio de sesión de Rails) con bcrypt, no hay una columna de sal en la base de datos, así que estoy confundido. Si la sal es aleatoria y no se almacena en ningún lado, ¿cómo podemos repetir de manera confiable el proceso de hash?

En resumen, ¿cómo puede bcrypt tener sales incorporadas ?

Nathan Long
fuente

Respuestas:

789

Esto es bcrypt:

Genera una sal al azar. Se ha preconfigurado un factor de "costo". Recoge una contraseña.

Derive una clave de cifrado de la contraseña utilizando el factor de sal y costo. Úselo para cifrar una cadena conocida. Almacene el costo, sal, y el texto cifrado. Debido a que estos tres elementos tienen una longitud conocida, es fácil concatenarlos y almacenarlos en un solo campo, y aún así poder separarlos más adelante.

Cuando alguien intenta autenticarse, recupere el costo almacenado y la sal. Derive una clave de la contraseña de entrada, el costo y la sal. Cifre la misma cadena conocida. Si el texto cifrado generado coincide con el texto cifrado almacenado, la contraseña es una coincidencia.

Bcrypt funciona de manera muy similar a los esquemas más tradicionales basados ​​en algoritmos como PBKDF2. La principal diferencia es su uso de una clave derivada para cifrar texto plano conocido; otros esquemas (razonablemente) suponen que la función de derivación de clave es irreversible y almacenan la clave derivada directamente.


Almacenado en la base de datos, un bcrypt"hash" podría verse así:

$ 2a $ 10 $ vI8aWBnW3fID.ZQ4 / zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa

Esto es en realidad tres campos, delimitados por "$":

  • 2aidentifica la bcryptversión del algoritmo que se utilizó.
  • 10es el factor de costo; Se utilizan 2 10 iteraciones de la función de derivación de teclas (lo cual no es suficiente, por cierto. Recomendaría un costo de 12 o más).
  • vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTaes la sal y el texto cifrado, concatenados y codificados en una Base-64 modificada. Los primeros 22 caracteres se decodifican a un valor de 16 bytes para la sal. Los caracteres restantes son texto cifrado que se comparará para la autenticación.

Este ejemplo está tomado de la documentación para la implementación de Ruby de Coda Hale.

erickson
fuente
77
¿Tendría más detalles sobre por qué el factor de costo de 10 no sería suficiente? En Grails, noté que 10 es el valor predeterminado para el factor de costo / rondas de registro para bcrypt, por lo que podría valer la pena actualizar su sugerencia.
pm_labs
57
El factor de costo para bcrypt es exponencial, o más bien, un factor de costo de 10 significa 2 ^ 10 rondas (1024), un factor de costo de 16 significaría 2 ^ 16 rondas (65536). Es natural que demore entre 5 y 10 segundos. Debería tomar aproximadamente 64 veces más tiempo que un factor de costo de 10. Para aclarar otra información errónea, la función de cifrado de PHP utiliza la biblioteca de cifrado de Unix que se implementa en c.
thomasrutter
3
@TJChambers Eso es correcto; Si puede establecer la contraseña en la cuenta, podrá autenticarse. El hash de contraseña no tiene la intención de prevenir ese ataque. Su objetivo es evitar que un atacante con acceso de solo lectura a la tabla de contraseñas se autentique. Por ejemplo, obtienes una cinta de respaldo con la tabla sobre ella.
erickson
8
@LobsterMan No, en realidad no. Si pudieras guardar un secreto, no usarías este enfoque, solo almacenarías la contraseña. Los esquemas de autenticación de contraseña se basan en la suposición de que el atacante ha descubierto todo lo que sabe. La sal está ahí para requerir que cada contraseña sea atacada individualmente. El esfuerzo computacional requerido para probar las contraseñas se rige por las iteraciones. Si los usuarios eligen buenas contraseñas, serán seguras, incluso cuando se revele la sal. Ocultar la sal podría ayudar a alguien con una contraseña incorrecta en algunos casos, pero primero trabajaría en la calidad de la contraseña.
erickson el
1
@NLV Es una cadena definida en la especificación bcrypt:"OrpheanBeholderScryDoubt"
erickson
182

Creo que esa frase debería estar redactada de la siguiente manera:

bcrypt tiene sales integradas en los hashes generados para evitar ataques de la tabla del arco iris.

La bcryptutilidad en sí misma no parece mantener una lista de sales. Por el contrario, las sales se generan aleatoriamente y se agregan a la salida de la función para que se recuerden más adelante (de acuerdo con la implementación de Javabcrypt ). Dicho de otra manera, el "hash" generado por bcryptno es solo el hash. Más bien, es el hash y la sal concatenados.

Adam Paynter
fuente
20
OK, así que me registro en un sitio y elijo la contraseña "foo". Bcryptagrega una sal aleatoria de "akd2! *", lo que resulta en "fooakd2! *", que se procesa y almacena. Más tarde, intento iniciar sesión con la contraseña "barra". Para ver si estoy en lo correcto, necesita hacer un hash "barakd2! *". Si la sal se generó al azar para comenzar, ¿cómo sabe cómo volver a agregarla a la "barra" antes de dividir y comparar?
Nathan Long
46
@Nathan: bcryptsabe cómo extraer la sal de la salida generada (que se almacena en la base de datos). Cuando llega el momento de autenticarse, bcryptsepara la salida original en sus componentes hash y salt. El componente de sal se aplica a la contraseña entrante ingresada por el usuario.
Adam Paynter
22
Para responder al comentario de Nathan Long, una buena forma de pensar en esto es que las sales no deben ser secretas. Esta es la razón por la cual la sal se incluye en la salida de la función bcrypt como una de las respuestas señaladas anteriormente. La sal está ahí para evitar las tablas de arcoíris, que son listas de contraseñas comunes, o simplemente fuerza bruta, etc. de diferentes contraseñas pero hash. Sin sal, el hash para una contraseña en la base de datos A sería lo mismo que un hash para una contraseña en la base de datos B. Salt simplemente cambia los valores de hash haciendo que sea más difícil para alguien que robó la base de datos descifrar (deshacer) contraseñas.
Joseph Astrahan el
11
@Nathan, pero ¿puede un atacante eliminar las sales conocidas en todas las contraseñas y luego crear una tabla con ellas?
Oscar
3
Así es como lo entiendo: la idea es que cada contraseña tiene una sal única. La sal está incorporada en el hash de la contraseña, por lo que un hacker tendría que crear una tabla de arcoíris para cada contraseña. Esto tomaría una enorme cantidad de tiempo para una base de datos moderada. Se trata de ralentizar a un atacante y, por lo tanto, hacer que la fuerza bruta no tenga sentido.
PVermeer
0

Para hacer las cosas aún más claras,

Dirección de registro / inicio de sesión ->

La contraseña + salt se cifra con una clave generada a partir de: cost, salt y la contraseña. llamamos a ese valor cifrado el cipher text. luego adjuntamos la sal a este valor y lo codificamos usando base64. adjuntando el costo y esta es la cadena producida a partir de bcrypt:

$2a$COST$BASE64

Este valor se almacena eventualmente.

¿Qué necesitaría hacer el atacante para encontrar la contraseña? (otra dirección <-)

En caso de que el atacante tenga control sobre la base de datos, el atacante decodificará fácilmente el valor base64, y luego podrá ver la sal. La sal no es secreta. Aunque es al azar. Entonces tendrá que descifrar el cipher text.

Lo que es más importante: no hay hashing en este proceso, sino cifrado costoso de la CPU: descifrado. así, las tablas de arcoíris son menos relevantes aquí.

jony89
fuente
-2

Esto es de la documentación de la interfaz PasswordEncoder de Spring Security,

 * @param rawPassword the raw password to encode and match
 * @param encodedPassword the encoded password from storage to compare with
 * @return true if the raw password, after encoding, matches the encoded password from
 * storage
 */
boolean matches(CharSequence rawPassword, String encodedPassword);

Lo que significa que uno tendrá que hacer coincidir la Contraseña sin procesar que el usuario ingresará nuevamente en el próximo inicio de sesión y la hará coincidir con la contraseña codificada de Bcrypt que se almacena en la base de datos durante el inicio de sesión / registro anterior.

Conoce a Shah
fuente
Esto no responde a la pregunta en absoluto ... No dice nada acerca de cómo bcrypt puede tener sales
incorporadas