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 10
como 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 |
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.