9

我看到一个建议,将轮数设置为($currentYear - 2000)考虑摩尔定律,这样 2013 年将是13轮数,因此是2^13总迭代。当然,您需要考虑您自己的硬件以确保它不会花费太长时间(我认为1 second推荐用于检查密码/哈希值是“安全的”,并且在我当前的硬件上该标记周围有 13 轮)。

对于社交网络类型的网站,这听起来合理吗?或者我是否会在将来通过使用设置自己进行非常慢的密码检查($currentYear - 2000)

另外,你如何处理从一年到下一年的轮数变化?更改轮数不会更改哈希值,因此不允许您在 2014 年检查 2013 年的哈希值,因为检查会使用额外的一轮?您是否必须每年重新计算每个哈希值,或者它是如何工作的?

4

3 回答 3

16

首先,我质疑该建议(根据年份调整成本)。成本应基于硬件的速度,而不是当前日期。如果您从现在到 2015 年之间不升级您的服务器,则没有理由增加成本。你所做的只是放慢一个已经很慢的过程。

话虽如此,我也质疑 1 秒对于大多数用途的建议。如果您正在处理高度敏感的信息,则 1 秒(或更长)就可以了。但对于一般的网站,我通常建议在 0.25 到 0.5 秒之间。在某些情况下,你可以降低,但我不会没有充分的理由。

现在,问题本身。使用crypt()orpassword_hash()时,迭代计数以返回哈希格式存储。事实上,盐也是如此。所以计算哈希所需的所有信息都包含在其中!

如果你没有使用这些 API(或我维护的 polyfill:password-compat),那么我真的想知道你为什么不使用。不要发明自己的密码加密。除非您有充分的理由(出于某些政府合规性原因,或与 PHP <= 5.2 的兼容性),否则不要使用使用本机哈希的库(如 phpass)。

一般认为 bcrypt 是当今最强的哈希格式。SCrypt 更强大,但它也存在一些问题,而且它仍然很新(而且它还没有在 PHP 核心中可用)。所以只需使用 bcrypt ...

password_hash()api 有一个机制让你做你所要求的:password_needs_rehash(). 基本上,你传入散列,以及你今天使用的选项,它会告诉你是否需要重新散列:

if (password_verify($password, $hash)) {
    if (password_needs_rehash($hash, PASSWORD_BCRYPT, ['cost' => 14])) {
        $hash = password_hash($password);
        update_password_in_database($hash);
    }
    $loggedin = true;
}

阅读有关 password_hash() 的 RFC 以了解有关它的更多信息(我从大量来源收集数据,并在 RFC 中包含参考资料)。

编辑 - 跟进@AnotherParker 的评论:

犯罪分子不会因为您没有升级服务器而停止升级他们的破解盒。您确实需要随着时间的推移增加工作参数以阻止离线攻击。

有点真实。嗯,是的,但错过了我上面所说的重点。

散列函数的成本参数是时间和精力的权衡。您需要花费一些时间来为每个散列增加额外的工作量。在相同的硬件上,花更多的时间会产生更多的工作。产生更多工作的另一种方法是获得更快的硬件。

但建议是在您当前的硬件上测试散列函数,并使其尽可能昂贵。如果 0.5 秒是您今天所能承受的最大值,除非您升级服务器硬件,否则增加成本对您有何帮助?简而言之,它不会因为你会打破你已经确定的最重要的时间限制。

所以你不能在不增加服务器能力的情况下增加工作参数,除非你已经产生了弱散列。

另外,请查看有关该主题的此答案

于 2013-03-27T16:19:18.130 回答
5

当您使用 bcrypt 时,轮数是生成的哈希的一部分:

crypt ( 'Password', '$2a$04$thisshallbemysalt' );

会导致类似

$2a$04$thisshallbemysalt.rAnd0ml0ok1ngch4rsh3re

2a在第一个 $ 符号之后代表 bcrypt 算法,下一个04代表轮数 - 因此通过查看哈希,您可以看到创建它的轮数。

因此,当您决定是时候增加轮数时,您可以检查用户登录时用于生成存储哈希的轮数 - 如果不是您当前的轮数,您可以在那里重新哈希他们的密码并然后,将其保存为新哈希(当然,检查他们的密码是否与现有哈希匹配之后;-))

于 2013-03-27T15:57:11.100 回答
4

密钥拉伸的想法是使暴力破解变得不可行,因为在每一轮计算散列需要攻击者系统上相同数量的额外时间。

如果需要 1 秒或 0.9 秒或 2.5 秒,这并不重要。这个想法是每秒暴力破解数百万个密码哈希是不可行的。这是重要的因素,而不是实际的轮数。

例如,如果您使用 SHA256 哈希,系统每秒可以执行 X(例如 1,000,000)次哈希。通过密钥拉伸(因此散列,例如,500 次),您可以将此系统降低到每个密码每秒 1,000,000 / 500 = 2,000 次尝试。您有效地将攻击者的速度减慢了 500 倍。如果您使用 750 回合,您……完全正确!使攻击者减速 750 倍。但是增加回合数也会影响您的系统/网站/应用程序,因此您不想“只是为了确定”而太高;用户会抱怨登录缓慢!

这源于这样一个事实,例如,SHA1/256/512、MD4/5 等针对速度进行了优化。你想要的是速度优化算法,这样你就可以减慢攻击者的速度。因此,每隔几年,您只需将轮数增加一些因素,即您的用户的登录时间仍然可以接受,但它会减慢攻击者的速度,以至于不值得尝试暴力破解哈希(或至少迫使他们专注于更少的帐户而不是所有帐户)。

正如CBroe 解释的那样,当你增加轮数时,你会重新计算。

我不知道谁提出了 2 ($currentYear - 2000)推荐(我很想看到一个来源!没关系,找到它)但如果你问我这完全是公牛。我建议您更仔细地阅读答案并检查此问题/答案

如果你的 bcrypt 需要 0.2 到 0.5 秒(如果你问我,这是一个可以接受的“延迟”登录时),这意味着攻击者可以在相同硬件的情况下每秒暴力破解大约 5 到 2 个哈希值,如果他/她可能会大量投资 5,000/2,000 或 5,000,000/2,000,000。在可接受的时间(甚至生命周期)内暴力破解整个 160 位 (SHA1)、256 位 (SHA256) 甚至 448 位 (bcrypt) 空间仍然不可行。

于 2013-03-27T16:02:42.243 回答