简洁版本
至少需要 250 毫秒来计算的迭代次数
长版
当 BCrypt 在 1999 年首次发布时,他们列出了实现的默认成本因素:
6的 bcrypt 成本意味着 64 轮 (2 6 = 64)。
他们还指出:
当然,人们选择的任何成本都应该不时重新评估
- 在 1976 年部署时,Unix
crypt
每秒可以散列少于 4 个密码。(每个密码 250 毫秒)
- 1977 年,在 VAX-11/780 上,
crypt
(MD5)每秒可以评估约 3.6 次。(每个密码 277 毫秒)
这让您了解了最初的实现者在编写它时所考虑的那种延迟:
但是,当然,你能站得越久越好。我见过10
的每个 BCrypt 实现都被用作默认成本。我的实现使用了它。我相信是时候将默认成本增加到 12 了。
我们已经决定,我们希望每个哈希的目标时间不少于 250 毫秒。
我的台式电脑是 Intel Core i7-2700K CPU @ 3.50 GHz。我最初在 2014 年 1 月 23 日对 BCrypt 实现进行了基准测试:
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 |
未来证明
它应该是一个固定的最小值,而不是一个固定的常数。
而不是让你的密码哈希函数是:
String HashPassword(String password)
{
return BCrypt.HashPassword(password, BCRYPT_DEFAULT_COST);
}
它应该是这样的:
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;
}
理想情况下,这将成为每个人的 BCrypt 库的一部分,因此与其依靠库的用户定期增加成本,不如定期增加成本本身。