什么是密码散列的理想 bcrypt 工作因素。
如果我使用 10 倍,在我的笔记本电脑上散列密码大约需要 0.1 秒。如果我们最终得到一个非常繁忙的站点,那么仅检查人们的密码就会变成大量工作。
也许使用 7 的工作系数会更好,将每次笔记本电脑登录的总密码哈希工作减少到大约 0.01 秒?
您如何在蛮力安全和运营成本之间做出权衡?
什么是密码散列的理想 bcrypt 工作因素。
如果我使用 10 倍,在我的笔记本电脑上散列密码大约需要 0.1 秒。如果我们最终得到一个非常繁忙的站点,那么仅检查人们的密码就会变成大量工作。
也许使用 7 的工作系数会更好,将每次笔记本电脑登录的总密码哈希工作减少到大约 0.01 秒?
您如何在蛮力安全和运营成本之间做出权衡?
请记住,该值存储在密码中:$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 的人。
至少需要 250 毫秒来计算的迭代次数
当 BCrypt 在 1999 年首次发布时,他们列出了实现的默认成本因素:
6的 bcrypt 成本意味着 64 轮 (2 6 = 64)。
他们还指出:
当然,人们选择的任何成本都应该不时重新评估
这让您了解了最初的实现者在编写它时所考虑的那种延迟:
但是,当然,你能站得越久越好。我见过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 库的一部分,因此与其依靠库的用户定期增加成本,不如定期增加成本本身。
该问题与 bcrypt 密码哈希的成本因子的最佳和实际确定有关。
在您为服务器计算用户密码哈希的系统上,您预计用户数量会随着时间的推移而增长,为什么不将用户拥有您的服务帐户的持续时间作为决定因素,可能包括他们的登录频率作为该决定的一部分。
Bcrypt 成本因子 = 6 +(用户成员资格年数或其某个因素),总成本有一个可选上限,可能会以某种方式被该用户的登录频率修改。
请记住,尽管使用这样的系统或任何系统来有效地确定成本因素必须考虑到计算该哈希的成本可以用作针对服务器本身的 DDOS 攻击的方法,方法是考虑任何增加成本的方法影响他们的进攻。