用于密码散列的 PBKDF2 参数

信息安全 密码 哈希 pbkdf2
2021-08-31 14:11:28

我使用带有 SHA-256 的 PBKDF2 来存储密码的哈希值。我使用以下参数:

number of iterations desired        = 1024
length of the salt in bytes         = 16
length of the derived key in bytes  = 4096 

但最近我发现很可能是参数选择不当。

例如wiki页面说:

标准是在 2000 年编写的,建议的最小迭代次数是 1000 .... 截至 2005 年,Kerberos 标准推荐 4096 次迭代

这意味着很可能我必须增加这个数字

该标准建议盐长度至少为 64 位

这意味着我的盐长度还可以太低(感谢 Neil Smithline)。但是在搜索标准时,我找不到关于推荐盐长度的提及。

当我查找派生密钥的长度并找到这个不错的答案时:

如果您使用 PBKDF2 进行密码散列,那么 12 字节的输出应该没问题,并且具有不可忽略的安全余量

它表明我拿了太大的数字,这可能没有意义。


所以我的问题是:任何人都可以为这种密码散列方案(截至 2016 年)显示好的参数(可能有一些理由/链接)。我还可以保证派生的密钥长度始终与我要求的长度相同吗?

3个回答

你的出发点

PBKDF2-HMAC-SHA-256

所需的迭代次数 = 1024

盐的长度(以字节为单位)= 16

派生密钥的长度(以字节为单位)= 4096


算法

好的 - PBKDF2-HMAC-SHA-256 是一个不错的选择,但如果您在任何现代 64 位 CPU 上运行,我强烈推荐 PBKDF2-HMAC-SHA-512 代替,因为 SHA-512 需要 64 位操作这会降低基于 GPU 的攻击者的优势,因为现代 GPU 不支持 64 位。


16 字节的盐就可以了,只要它是加密随机的并且每个密码都是唯一的。正常建议在 12-24 字节范围内,其中 16 个被认为是相当可靠的。


输出和迭代

4096 字节的输出非常糟糕! SHA-256 的本机输出大小为 32 字节。 PBKDF2/RFC2898指出,在这种情况下,您将首先运行 1024 次迭代以获得第一个(最左边的)32 个字节,然后再运行 1024 次迭代以获得接下来的 32 个字节,依此类推,总共进行 128 次以获得完整的 4096 个字节您要求的输出。

因此,您总共进行了 131072 次迭代,得到了 4096 字节的输出。攻击者将总共进行 1024 次迭代,并将它们的 32 字节输出与输出的第一个(最左边)32 字节进行比较——如果它们相同,那么他们猜对了! 你只是给了每个攻击者 128:1 的优势!

相反,如果您对速度感到满意,您应该使用 32 字节的输出进行 131072 次迭代 - 您将花费与现在相同的时间(所以它是免费的!),您的攻击者将需要花费 128 倍以上时间比他们现在!

  • 注意:如果您迁移到 PBKDF2-HMAC-SHA-512,您最多可以使用 64 字节的输出,并且每次迭代将花费稍长的时间。

永远不要获得比散列函数的本机输出更多的密码散列输出,即 SHA-1 为 20 个字节,SHA-256 为 32 个字节,SHA-512 为 64 个字节。您可以选择存储更少,但它不会为您节省任何计算。我建议存储至少 28 字节的输出(224 位,是 112 位的两倍,3DES 的标称安全性)。

请注意,输出长度值是纯二进制输出——在你使用 BASE64 或十六进制之前,如果你这样做(我个人会存储原始二进制文件——它使用更少的空间,并且需要更少的转换)。

  1. 8 字节的盐很好,因为它在随机生成时是全局唯一的,这就是盐的目的:唯一性。太大不是问题,所以估计偏高是可以的,除非你浪费了一点存储空间。

  2. 32 字节对于派生密钥长度来说是好的(大多数散列算法将输出 16 到 64 字节之间,其中 64 只是过大了)。您的 4096 值绝对是矫枉过正——我实际上希望您将字节误认为是位!

  3. 迭代非常简单:在您的硬件上尽可能多。如果登录可能需要半秒,那么在它通过半秒之前你可以做多少次迭代。在 2020 年,400 000对于低端服务器来说是一个合理的值(平均服务器 CPU 可以处理更多的迭代)。

  4. 如果您必须选择一个,SHA-256是一个很好的算法。避免 MD5、SHA-1 和任何其他已知攻击。为此目的,它们可能是安全的(甚至是 MD5),但攻击只会变得更好,您最好不要使用已经部分损坏的东西。

如果您在动力不足的系统(例如 Raspberry Pi)上运行,您可能需要根据安全程度考虑不同的硬件,或者让登录需要更长的时间。无论如何,通常每天只进行一次登录,因此等待 10 秒可能是合理的,具体取决于您的应用程序。

我的第一个建议是使用您平台的默认密码存储机制。像 PHP 的password_hash这样的函数可以消除密码存储的所有烦恼。

假设你不能这样做,我建议使用 bcrypt,或者甚至是更新的 scrypt 函数。在 GPU 上使用并行操作时,bcrypt 更难破解。scrypt 对内存和 CPU 征税。这个答案解释了这一切都是非常详细的。

假设您出于某种原因被 pbkdf2 卡住了......

Salt:根据Thomas Pornin 在2013 年的回答,128 位或 16 字节就足够了。Crackstation的建议是 192 位或 24 字节。我认为该范围内的任何地方都是安全的。就个人而言,我会选择 24 个字节,因为盐长度(假设您不挂在生成随机数据上)对哈希性能没有真正的贡献。确保使用CSPRNG生成盐。

密码长度: Thomas Pornin 的回答建议使用 12 个字节,Crackstation 24。同样,我倾向于使用更大的大小,但认为该范围内的任何内容都是安全的。

迭代次数:迭代次数应尽可能多。所以开始测试散列的性能。我已经看到了 0.05-0.10 秒的建议,但认为这些只是估计值。如果你有多余的计算资源,也许你可以走得更高一点(太高会使认证速度太慢)。如果您的计算资源稀缺,请选择更小。

概括:

number of iterations desired        = max you can tolerate
length of the salt in bytes         = 16-24
length of the derived key in bytes  = 12-24