Factor de trabajo óptimo de bcrypt

81

¿Cuál sería un factor de trabajo bcrypt ideal para el hash de contraseñas?

Si utilizo un factor de 10, se necesitan aproximadamente 0,1 segundos para escribir una contraseña en mi computadora portátil. Si terminamos con un sitio muy ocupado, eso se convierte en una gran cantidad de trabajo simplemente verificar las contraseñas de las personas.

¿Quizás sería mejor usar un factor de trabajo de 7, reduciendo el trabajo total de hash de contraseña a aproximadamente .01s por inicio de sesión de computadora portátil?

¿Cómo decide el equilibrio entre la seguridad de la fuerza bruta y el costo operativo?

Chris
fuente
7
El costo frustra los ataques fuera de línea. Cuando está "en línea", puede utilizar un retardo mínimo entre intentos (por ejemplo, 5 segundos) para evitar un ataque de denegación de servicio.
Ian Boyd
3
Duplicado en InformationSecurity: Cantidad recomendada de rondas para bcrypt
Christian Strempfer
1
Para cualquier persona interesada, acabo de escribir una pequeña herramienta CLI de Java para probar el rendimiento de bcrypt en los servidores (que obviamente es importante para equilibrar la seguridad, la carga del servidor y los tiempos de respuesta): github.com/cdraeger/hash-performance
Blacklight

Respuestas:

103

Recuerde que el valor se almacena en la contraseña: $2a$(2 chars work)$(22 chars salt)(31 chars hash). No es un valor fijo.

Si encuentra que la carga es demasiado alta, simplemente hágalo para que la próxima vez que inicien sesión, se cifre en algo más rápido de calcular. Del mismo modo, a medida que pasa el tiempo y obtienes mejores servidores, si la carga no es un problema, puedes actualizar la fuerza de su hash cuando inician sesión.

El truco es hacer que tome aproximadamente la misma cantidad de tiempo para siempre en el futuro junto con la Ley de Moore. El número es log2, así que cada vez que las computadoras dupliquen su velocidad, agregue 1 al número predeterminado.

Decide cuánto tiempo quieres que tarde en aplicar la fuerza bruta a la contraseña de un usuario. Para algunas palabras comunes del diccionario, por ejemplo, la creación de su cuenta probablemente ya les advirtió que su contraseña era débil. Si es una de las 1000 palabras comunes, digamos, y un atacante necesita 0.1 segundos para probar cada una, eso les compra 100 (bueno, algunas palabras son más comunes ...). Si un usuario elige 'palabra común del diccionario' + 2 números, eso es más de dos horas. Si su base de datos de contraseñas se ve comprometida y el atacante solo puede obtener unos pocos cientos de contraseñas al día, ha comprado a la mayoría de sus usuarios horas o días para cambiar sus contraseñas de forma segura. Es cuestión de ganarles tiempo.

http://www.postgresql.org/docs/8.3/static/pgcrypto.html tiene algunas ocasiones para descifrar contraseñas para que las considere. Por supuesto, las contraseñas que enumeran son letras aleatorias. Diccionario de palabras ... Prácticamente hablando, no puedes salvar al tipo cuya contraseña es 12345.

Zer
fuente
8
Esta es realmente una excelente respuesta. Ni siquiera había considerado la idea de volver a criptar al iniciar sesión. Muchas gracias!
Chris
1
¿Cómo funcionaría el recrypt? Tendría que almacenar el costo de trabajo de bcrypt antiguo en algún lugar para poder usarlo para iniciar sesión y luego, después de validar su contraseña, actualizaría el hash y el costo en la base de datos.
Jerry Saravia
6
@JerrySaravia La belleza de bcrypt es que el costo se almacena dentro del propio hash, por lo que no necesita almacenar nada adicional. Simplemente autentíquese con el hash actual, luego vuelva a generar inmediatamente el hash con un costo diferente. ¡Simple!
Mark Locker
@MarkLocker, ¡Gracias Mark! ¡Es una hermosa cripta! Pero en serio, eso facilita mucho las cosas y es algo maravilloso.
Jerry Saravia
De acuerdo, ya que no puedo editarlo porque es un "intento incorrecto de responder" (¿ustedes siquiera leyeron mis ediciones?), Permítanme comentar la información. Valor de ejemplo: $2y$08$fJS4yx0i8kiOzIBIamZ51OWTMrzyE/4je34Oxhw.5xxp3Es7Ke32W. Razón por la que intenté editar: No estaba claro para mí si "2 caracteres funcionan" era un dígito o hexadecimal o algo así, tenía que probarlo. Aquí está el resultado de la prueba para todos los demás para que no tenga que probarlo usted mismo.
Luc
3

Version corta

El número de iteraciones que da al menos 250 ms para calcular

Versión larga

Cuando se publicó por primera vez BCrypt, en 1999, enumeraron los factores de costo predeterminados de su implementación:

  • usuario normal: 6
  • superusuario: 8

Un costo de bcrypt de 6 significa 64 rondas (2 6 = 64).

También señalan:

Por supuesto, cualquier costo que la gente elija debe reevaluarse de vez en cuando

  • En el momento de la implementación en 1976, Crypt podía codificar menos de 4 contraseñas por segundo. (250 ms por contraseña)
  • En 1977, en un VAX-11/780, la cripta (MD5) podía evaluarse unas 3,6 veces por segundo. (277 ms por contraseña)

Eso le da una idea del tipo de retrasos que los implementadores originales estaban considerando cuando lo escribieron:

  • ~ 250 ms para usuarios normales
  • ~ 1 segundo para superusuarios.

Pero, por supuesto, cuanto más tiempo puedas estar de pie, mejor. Cada implementación de BCrypt que he visto se usa 10como costo predeterminado. Y mi implementación usó eso. Creo que es hora de que aumente el costo predeterminado a 12.

Hemos decidido que queremos apuntar a no menos de 250 ms por hash.

Mi PC de escritorio es una CPU Intel Core i7-2700K a 3,50 GHz. Originalmente comparé una implementación de BCrypt el 23/1/2014:

1/23/2014  Intel Core i7-2700K CPU @ 3.50 GHz

| Cost | Iterations        |    Duration |
|------|-------------------|-------------|
|  8   |    256 iterations |     38.2 ms | <-- minimum allowed by BCrypt
|  9   |    512 iterations |     74.8 ms |
| 10   |  1,024 iterations |    152.4 ms | <-- current default (BCRYPT_COST=10)
| 11   |  2,048 iterations |    296.6 ms |
| 12   |  4,096 iterations |    594.3 ms |
| 13   |  8,192 iterations |  1,169.5 ms |
| 14   | 16,384 iterations |  2,338.8 ms |
| 15   | 32,768 iterations |  4,656.0 ms |
| 16   | 65,536 iterations |  9,302.2 ms |

ingrese la descripción de la imagen aquí

Prueba de futuro

En lugar de tener una constante fija, debería ser un mínimo fijo .

En lugar de tener la función hash de su contraseña:

String HashPassword(String password)
{
   return BCrypt.HashPassword(password, BCRYPT_DEFAULT_COST);
}

debería ser algo como:

String HashPassword(String password)
{  
   /*
     Rather than using a fixed default cost, run a micro-benchmark
     to figure out how fast the CPU is.
     Use that to make sure that it takes **at least** 250ms to calculate
     the hash
   */
   Int32 costFactor = this.CalculateIdealCost();
   //Never use a cost lower than the default hard-coded cost
   if (costFactor < BCRYPT_DEFAULT_COST) 
      costFactor = BCRYPT_DEFAULT_COST;

   return BCrypt.HashPassword(password, costFactor);
}

Int32 CalculateIdealCost()
{
    //Benchmark using a cost of 5 (the second-lowest allowed)
    Int32 cost = 5;

    var sw = new Stopwatch();
    sw.Start();
    this.HashPassword("microbenchmark", cost);
    sw.Stop();

    Double durationMS = sw.Elapsed.TotalMilliseconds;

    //Increasing cost by 1 would double the run time.
    //Keep increasing cost until the estimated duration is over 250 ms
    while (durationMS < 250)
    {
       cost += 1;
       durationMS *= 2;
    }

    return cost;
}

E idealmente, esto sería parte de la biblioteca BCrypt de todos, por lo que en lugar de depender de los usuarios de la biblioteca para aumentar periódicamente el costo, el costo aumenta periódicamente.

Ian Boyd
fuente