推荐的 bcrypt 轮数

信息安全 bcrypt
2021-09-05 01:24:25

现在(2012 年 7 月)推荐的 bcrypt 轮数用于散列普通网站的密码(仅存储姓名、电子邮件地址和家庭住址,但不存储信用卡或医疗信息)?

换句话说,bcrypt密码破解社区目前的能力如何?几个 bcrypt 库使用 12 轮(2^12 次迭代)作为默认设置。这是推荐的工作因数吗?6 轮是否不够强(这恰好是 Javascript 中客户端 bcrypt 散列的限制,另请参阅挑战挑战:客户端密码散列和服务器端密码验证)?

我已阅读答案https://security.stackexchange.com/a/3993/11197,它深入讨论了如何平衡各种因素(尽管是 PBKDF2-SHA256)。但是,我正在寻找一个实际的数字。一个经验法则。

3个回答

简洁版本

至少需要 250 毫秒来计算的迭代次数

长版

当 BCrypt 在 1999 年首次发布时,他们列出了实现的默认成本因素:

  • 普通用户:6
  • 超级用户:8

6的 bcrypt 成本意味着 64 轮 (2 6 = 64)。

他们还指出:

当然,人们选择的任何成本都应该不时重新评估

  • 在 1976 年部署时,Unixcrypt每秒可以散列少于 4 个密码。(每个密码 250 毫秒)
  • 1977 年,在 VAX-11/780 上,crypt(MD5)每秒可以评估约 3.6 次。(每个密码 277 毫秒)

这让您了解了最初的实现者在编写它时所考虑的那种延迟:

  • 普通用户约 250 毫秒
  • 超级用户约 1 秒。

但是,当然,你能站得越久越好。我见过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 库的一部分,因此与其依靠库的用户定期增加成本,不如定期增加成本本身。

我认为您所有问题的答案都已包含在Thomas Pornin 的答案中。你链接到它,所以你大概知道它,但我建议你再读一遍。

基本原则是:不要选择多轮;相反,选择密码验证将在您的服务器上花费的时间,然后根据此计算轮数。您希望验证尽可能长时间。

有关具体数字的一些示例,请参阅 Thomas Pornin 的回答。他建议一个合理的目标是密码验证/散列每个密码需要 241 毫秒。(注意:Thomas 最初写的是“8 毫秒”,这是错误的——这是一天而不是一个月的耐心的数字。)这仍然可以让您的服务器每秒验证 4 个密码(如果您可以在平行线)。Thomas 估计,如果这是您的目标,那么大约 20,000 发子弹就在正确的范围内。

但是,最佳轮数将随您的处理器而变化。理想情况下,您将基准测试处理器需要多长时间并相应地选择数字。这不需要那么长时间。因此,为了获得最佳结果,只需编写脚本并计算出需要多少轮才能确保密码散列在您的服务器上花费大约 240 毫秒(或者更长,如果您能忍受的话)。

Stronger Key Derivation via Sequential Memory-Hard Functions是一篇关于密钥拉伸主题的非常好的论文。在第 14 页,它比较了各种散列算法和破解散列所需的成本,这是思考这些问题的有用方法。(附带说明,如果 TPM 不可用,ChromeOS 会使用 Scrypt。)

这个想法是您希望这些密码哈希尽可能长时间地不间断。根据摩尔定律,这是一个指数级快速移动的目标。Scrypt 使用可变数量的内存和 cpu,这个变量可能会随着时间的推移而变得更重。每次客户端登录时,您都可以更新密码哈希以更安全。在 PBKDF2 的情况下,这可能看起来像rounds=2^(current_year-2000)或类似的东西。

重要的是要注意,您不能只是将此处理卸载到客户端并期望您的协议是安全的。我所知道的所有客户端散列身份验证协议都要求服务器进行相同的计算以验证身份验证凭据(NTLM、NTLMv2、SRP、WPA-PSK ......)。