Número recomendado de rondas para bcrypt

70

¿Cuál es hoy en día (julio de 2012) el número recomendado de rondas de bcrypt para hashear una contraseña para un sitio web promedio (que almacena solo el nombre, la dirección de correo electrónico y la dirección del hogar, pero no la tarjeta de crédito ni la información médica)?

En otras palabras, ¿cuál es la capacidad actual de la comunidad de craqueo de contraseñas de bcrypt? Varias bibliotecas bcrypt utilizan 12 rondas (2 ^ 12 iteraciones) como configuración predeterminada. ¿Es ese el factor de trabajo recomendado? Si 6 rondas no fueran lo suficientemente fuertes (lo que resulta ser el límite para el hash bcrypt del lado del cliente en Javascript, vea también Desafío desafiante: hash de la contraseña del lado del cliente y verificación de la contraseña del lado del servidor )?

He leído la respuesta enlace que proporciona una discusión detallada sobre cómo equilibrar los diversos factores (aunque para PBKDF2-SHA256). Sin embargo, estoy buscando un número real. Una regla de oro.

    
pregunta Jason Smith 14.07.2012 - 19:51
fuente

3 respuestas

25

Creo que la respuesta a todas sus preguntas ya se encuentra en la respuesta de Thomas Pornin . Usted se vinculó con él, por lo que probablemente lo sepa, pero le sugiero que lo lea de nuevo.

Los principios básicos son: no elijas una serie de rondas; en su lugar, elija la cantidad de tiempo que tomará la verificación de contraseña en su servidor, luego calcule la cantidad de rondas basándose en eso. Desea que la verificación dure todo el tiempo que pueda permanecer.

Para algunos ejemplos de números concretos, vea la respuesta de Thomas Pornin. Él sugiere que un objetivo razonable sería que la verificación / hashing de la contraseña tome 241 milisegundos por contraseña. (Nota: Thomas escribió inicialmente "8 milisegundos", lo cual es incorrecto: esta es la cifra para una paciencia de un día en lugar de un mes). Eso aún le permite a su servidor verificar 4 contraseñas por segundo ( Más si puedes hacerlo en paralelo). Thomas estima que, si este es su objetivo, aproximadamente 20,000 rondas están en el estadio correcto.

Sin embargo, el número óptimo de rondas cambiará con su procesador. Lo ideal sería comparar el tiempo que demora su procesador y elegir el número correspondiente. Esto no toma tanto tiempo; así que para obtener los mejores resultados, simplemente levante el script y descubra cuántas rondas son necesarias para garantizar que el hashing de la contraseña demore aproximadamente 240 milisegundos en su servidor (o más, si puede soportarlo).

    
respondido por el D.W. 16.07.2012 - 00:57
fuente
49

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

  • usuario normal: 6
  • superusuario: 8

También tienen en cuenta:

  

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

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

Si usamos ese valor inicial de "usuario normal" , queremos intentar ajustar la inflación de la potencia de cálculo ( suponiendo que en promedio se duplica cada 18 meses ).

  

R = R 0 × 2 (meses / 18)
  R = 64 × 2 (meses / 18)

Hoy (9 de marzo de 2015) han pasado 171 meses desde el 31 de diciembre de 1999 (o usar 1/1/2000 por simplicidad), el número de rondas se debería haber duplicado un poco más de 9 veces:

  

R = 64 × 2 (171/18)
  R = 64 × 2 9.5
  R = 64 × 724.1
  R = 46,341.0

Al final, queremos convertir eso de nuevo a un factor de costo

  

costo = ln (R) / ln (2)
  costo = ln (46,341.0) / ln (2)
  costo = 15.5

La practicidad de un factor de costo o 15 depende de la potencia de cálculo de su servidor. Por ejemplo, mi PC de escritorio es una CPU Intel Core i7-2700K a 3.50 GHz. Originalmente hice una evaluación comparativa de una implementación de BCrypt el 23 de enero de 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 |

Pero eso fue 2014

Esos cronogramas se calcularon originalmente a principios de 2014. En mi cálculo solo deberían haberse utilizado 156 meses (en lugar de 171):

  

R = 64 × 2 (156/18)
  R = 64 × 2 8.66
  R = 64 × 406.8
  R = 26,035.2

     

costo = ln (R) / ln (2)
  costo = ln (26,035.2) / ln (2)
  costo = 14.7

Pero el i7-2700K ya se ha suspendido

El i7-2700K ya se había suspendido (Q1 2013) cuando ejecuté mis puntos de referencia. Se lanzó, y fue de vanguardia, en el cuarto trimestre de 2011. Si ejecuto los números para el cuarto trimestre de 2011:

  

R = 64 × 2 (129/18)
  R = 64 × 2 7.16
  R = 64 × 143.7
  R = 9,196.8

     

costo = ln (R) / ln (2)
  costo = ln (9,196.8) / ln (2)
  costo = 13.2

Un costo de 13 es, en mi escritorio, casi 2 segundos en un segundo.

¿Cuánto tiempo puede soportarlo?

Eso le da una idea del tipo de retrasos que los implementadores originales estaban considerando cuando lo escribieron: ~ 0.5-1 segundo.

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

Pruebas futuras

También podría cambiar la función hash:

hash = HashPassword("correct battery horse stapler");

es decir, en el que confía en el costo predeterminado, para utilizar en cambio un costo que se desliza automáticamente. De esta manera el costo se incrementa con el tiempo. Cambio:

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

a 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;
}

Editar 12/03/2015 : números de velocidad actualizados. El compilador de 32 bits de Delphi XE6 (c.2013) genera un código de un orden de magnitud más rápido que Delphi 5 (c.1999) para el mismo procesador. El compilador Delphi XE6 de 64 bits genera un código un 20% más lento que el compilador de 32 bits.

    
respondido por el Ian Boyd 09.03.2015 - 16:31
fuente
3

Derivación de clave más fuerte a través de funciones secuenciales de memoria difícil es un excelente artículo sobre el tema del estiramiento de la clave. En la página 14 compara varios algoritmos de hash con la cantidad de dinero que costará romper el hash, lo cual es una forma útil de pensar en estas cosas. (En una nota al margen, ChromeOS usa Scrypt si TPM no está disponible).

La idea es que desee que estos hashes de contraseña permanezcan intactos el mayor tiempo posible. Con la ley de Moore, este es un objetivo en movimiento exponencialmente rápido. Scrypt usa una cantidad variable de memoria y CPU, esta variable podría volverse más pesada en función del tiempo. En que cada vez que el cliente inicie sesión, puede actualizar el hash de la contraseña para que sea más seguro. En el caso de PBKDF2, esto podría parecer rounds=2^(current_year-2000) o algo así.

Es importante tener en cuenta que no puede simplemente descargar este procesamiento en el cliente y esperar que su protocolo sea seguro. Todos los protocolos de autenticación de hash del lado del cliente que conozco requieren que el servidor realice un cálculo idéntico para verificar las credenciales de autenticación (NTLM, NTLMv2, SRP, WPA-PSK ...).

    
respondido por el rook 14.07.2012 - 22:15
fuente

Lea otras preguntas en las etiquetas