最佳bcrypt工作因子


80

对于密码哈希来说,理想的bcrypt工作因素是什么。

如果我使用10的系数,则在笔记本电脑上哈希密码大约需要0.1秒。如果我们的站点非常繁忙,那么仅检查人们的密码就需要进行大量工作。

也许使用7的工作因子会更好,将每台笔记本电脑登录的总密码哈希工作减少到大约.01s?

您如何确定暴力安全性和运营成本之间的权衡?


7
代价阻止了离线攻击。当处于“联机”状态时,您可以使用两次尝试之间的最小延迟(例如5秒)来防止拒绝服务攻击。
伊恩·博伊德


1
对于感兴趣的任何人,我只是写了一个小型Java CLI工具来测试服务器上的bcrypt性能(这对于平衡安全性,服务器负载和响应时间很重要):github.com/cdraeger/hash-performance
Blacklight

Answers:


103

请记住,该值存储在密码:中$2a$(2 chars work)$(22 chars salt)(31 chars hash)。它不是固定值。

如果您发现负载太高,只需进行设置,以便下次他们登录时可以加密出更快的计算量。同样,随着时间的流逝,您将获得更好的服务器,如果负载不是问题,则可以在其登录时提升其哈希强度。

诀窍是让它与摩尔定律一起在未来永远占据大致相同的时间。该数字为log2,因此每次计算机速度加倍时,请将默认数字加1。

确定暴力破解用户密码所需的时间。例如,对于某些常见的词典单词,您的帐户创建可能已经警告他们密码不正确。举例来说,如果它是1000个常用词之一,而攻击者需要0.1秒钟来测试每个单词,那么他们将购买100个常用词(嗯,有些词更常见...)。如果用户选择“通用词典词” + 2个数字,则超过两个小时。如果您的密码数据库遭到破坏,并且攻击者每天只能获得几百个密码,则您已花了数小时或数天的时间购买了大部分用户,以安全地更改其密码。这是为他们争取时间的问题。

http://www.postgresql.org/docs/8.3/static/pgcrypto.html有很多时间可以破解密码供您考虑。当然,他们列出的密码是随机字母。词典单词...实际上,您不能保存密码为12345的家伙。


8
这确实是一个很好的答案。我什至没有考虑过重新登录的想法。非常感谢!
克里斯,

1
加密将如何工作?您将不得不将旧的bcrypt工作成本存储在某个地方,以便可以使用它来登录它们,然后在验证其密码之后,您将更新数据库中的哈希值和成本吗?
杰里·萨拉维亚

6
@JerrySaravia bcrypt的优点在于,其成本存储在散列自身中-因此您不需要存储任何额外的内容。只需使用当前哈希进行身份验证,然后立即以不同的成本重新生成哈希。简单!
Mark Locker

@MarkLocker,谢谢马克!这是一个B [美丽]密码!但是说真的,这使事情变得容易得多,这是一件了不起的事情。
杰里·萨拉维亚

好的,因为我是因为“不正确的回复尝试”(你们甚至看过我的编辑?)而不允许对其进行编辑,所以请允许我对信息进行评论。值示例:$2y$08$fJS4yx0i8kiOzIBIamZ51OWTMrzyE/4je34Oxhw.5xxp3Es7Ke32W。我尝试编辑的原因:我不清楚“ 2个字符的工作方式”是数字还是十六进制之类的东西,必须进行测试。这是其他所有人的测试结果,因此您不必自己尝试一下。
吕克2014年

3

精简版

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

长版

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

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

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

他们还指出:

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

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

这使您感受到原始实现者在编写它们时所考虑的那种延迟:

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

但是,当然,您能站立的时间越长越好。我见过的每个BCrypt实现都用作10默认成本。而我的实现使用的。我认为现在是时候将默认费用增加到12了。

我们已经决定要针对每个哈希不少于250ms。

我的台式机是@ 3.50 GHz的Intel Core i7-2700K CPU。我最初在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库的一部分,因此,不依赖库的用户来定期增加成本,而是定期自己增加成本。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.