按优先顺序,散列密码:
- Argon2
- scrypt
- bcrypt
- PBKDF2
不要将密码存储在:
- MD5
- md5crypt
- sha512crypt
- sha256crypt
- UNIX crypt(3)
- SHA-1/2/3
- Skein
- BLAKE2
- 任何通用散列函数。
- 任何加密算法。
- 你自己的设计。
- 纯文本
介绍
除了帐户和密码的不断数据库泄漏之外,加密圈子中经常出现的东西是散列密码。由于“散列密码”一词,可能不太了解的开发人员会想到使用通用的单向固定长度抗冲突加密散列函数,例如 MD5、SHA-1、SHA-256 或 SHA-512,没有考虑这个问题。当然,使用这些函数是有问题的,因为它们很快。事实证明,我们不喜欢快速散列函数,因为密码破解者确实喜欢快速散列函数。他们做得越快,他们就能越早恢复密码。
问题
因此,密码社区没有使用 MD5、SHA-1、SHA-256、SHA-512 等,而是聚集在一起,引入了专门设计的密码散列函数,其中包含自定义工作因素作为成本。另外,密钥派生函数也被设计用于创建加密密钥,其中自定义工作因素也包括在此处作为成本。因此,通过基于密码的密钥派生函数和专门设计的密码散列函数,我们提出了一些您应该使用的算法。
解决方案
这种类型最流行的算法将包括,按照我个人的偏好,从最喜欢到最不喜欢:
- Argon2 (KDF)
- scrypt (KDF)
- bcrypt
- PBKDF2 (KDF)
KDF 和密码散列函数之间的唯一区别在于,KDF 的摘要长度可以是任意的,而密码散列函数将具有固定长度的输出。
更新:
Argon2 经受住了时间的考验,应该被视为最佳实践。如果 Argon2 无法部署,scrypt 会提供内存硬度,这使得 ASIC 和 FPGA 无法承受。bcrypt 和 PBKDF2 不提供内存硬度,但为 CPU 提供可调的工作因子。请注意,我曾经提倡 sha256crypt 和 sha512crypt。我不再提供这个建议。看到这个帖子为什么。
很长一段时间以来,我都不喜欢 scrypt 作为密码散列函数。我想我已经改变主意了。尽管 scrypt 对所选择的参数很敏感,并且存在时间内存权衡 (TMTO) 问题,但只要您选择合理的默认值,它仍然被认为是安全的。我还将 bcrypt 放在 Argon2 上,因为 Argon2 最近刚刚被宣布为密码哈希竞赛的获胜者。与所有密码原语一样,我们需要时间来分析、攻击和挑选设计。如果大约 5 年后,它仍然坚固且安全,那么可以推荐它作为生产解决方案。与此同时,这当然值得测试,但可能不适用于生产代码。最后,我更喜欢 sha512crypt 和 sha256crypt 而不是 PBKDF2,主要是因为它们默认包含在每个 GNU/Linux 发行版中,它们基于强大的 SHA-2 散列函数,该函数已经进行了多年和大量的分析,并且与 PBKDF2 不同,您确切知道使用了哪个散列函数。PBKDF2 可以默认使用 SHA-2 函数,也可以使用 SHA-1。你需要检查你的图书馆以确定。
物以类聚,人以群分
无论如何,上述所有函数都包含成本参数,用于控制从密码计算散列所需的时间。成本参数到底是什么并不重要,更重要的是您要确定合适的时间来计算成本并创建散列。这意味着您需要识别您的威胁模型和您的对手。
您会发现自己处于两种常见的场景中,它们是:
- 密码存储
- 加密密钥
对于密码存储,您的威胁模型很可能是密码数据库泄露到 Internet,而世界各地的密码破解者都在使用数据库中的哈希来恢复密码。因此,您的对手是恶意软件、匿名者和密码破解者。对于加密密钥,您的威胁模型可能是私有加密密钥被泄露和侧信道攻击。因此,您的对手也是恶意软件、糟糕的密钥交换或不受信任的网络。了解您的威胁模型和对手会改变您处理问题的方式。
使用密码存储,您可能正在处理交互式登录,例如通过网站。因此,您可能希望密码散列时间更快,同时仍保持一个工作因素,以阻止对您泄露的数据库进行大规模分布式攻击。可能是 0.5 秒。这意味着如果数据库被泄露,密码破解者每秒最多只能破解 2 个密码。当您将此与 GPU 可以在 Windows NTLM 密码上执行的每秒数百万个哈希值进行比较时,每秒 2 个密码非常有吸引力。对于加密密钥,您可能不需要担心交互式会话,因此花 5 秒钟从密码创建密钥可能不是一件坏事。因此,密钥破解者每次猜测花费 5 秒试图恢复创建加密私钥的密码真的很好。
bcrypt、sha256crypt、sha512crypt 和 PBKDF2
那么,知道工作因素后,上述算法会是什么样子呢?下面,我看一下 bcrypt、sha256crypt、sha512crypt 和 PBKDF2 及其适当的成本。我突出显示了绿色行,其中可能的工作因素可能意味着花费 0.5 秒对密码进行哈希处理,而红色行可能意味着可能的工作因素可能意味着花费 5 整秒来创建基于密码的加密密钥。
请注意,对于 bcrypt,这意味着对于密码散列,因子 13 将提供大约 0.5 秒的成本来散列密码,而因子 16 将使我接近创建基于密码的大约 5 秒的成本钥匙。对于 sha256crypt、sha512crypt 和 PBKDF2,这似乎分别是大约 640,000 和 5,120,000 次迭代。
scrypt
当我们转向 scrypt 时,事情变得更加困难。使用 bcrypt、sha256crypt、sha512crypt 和 PBKDF2,我们的成本完全是 CPU 负载因素。不幸的是,虽然快速 GPU 集群可能存在问题,但它们仍然成为特定算法 FPGA 和 ASIC 的牺牲品。为了解决这个问题,我们还需要包括内存成本,因为这些设备上的内存似乎很昂贵。然而,同时拥有 CPU 和 RAM 成本意味着需要调整多个旋钮。因此,scrypt 的设计者 Colin Percival 决定捆绑 CPU 和 RAM 的成本三个因素:“N”、“r”和“p”。产生的内存使用量计算如下:
以字节为单位的内存 = (N * r * 128) + (r * p * 128)
关于什么是“最佳实践”有很多建议。似乎您至少应该使用 scrypt 具有以下成本因素,它提供 16 MiB 的内存负载:
- N: 16384 (214)
- r: 8
- p: 1
虽然您应该意识到 scrypt 参数的敏感性,但前提是您使用至少 16 MiB 的 RAM,但您并不比其他密码散列函数或 KDF 差。因此,在下表中,我通过调整三个参数来增加散列所需的内存成本。
2016-06-29 更新:我已经在后续帖子中阐明了这些参数,您绝对应该在https://pthree.org/2016/06/29/further-investigation-into-scrypt-and -argon2-密码哈希/。
因为我只能在这台测试机器中使用单插槽四核 CPU,所以我想将我的“p”成本限制为 1、2 和 4,这些表中显示了这些值。此外,我的 RAM 受到限制,并且不想破坏机器上运行的其他应用程序和服务,因此我将“r”成本限制为 4、8 和 16 乘以 128 字节( 512 字节、1024 字节和 2048 字节)。
有趣的是,Colin Precival 建议 16 MiB (N=16384 (2 14 ), r=8, p=1) 用于交互式登录,16 MiB (N=131072 (2 17 ), r=1, p=1) 用于对称登录密钥推导。如果我的目标是 0.5 秒的密码散列时间,那么我可以将其提高到 256 MiB (N=65536 (2 16 ), r=8, p=1) 或 2 GiB (N=2097152 (2 21 ), r =8, p=1),如果针对对称密钥派生的目标只是略多于 5 秒。
Argon2
最后,我们看看 Argon2。Argon2 有两种版本——Argon2d 和 Argon2i;第一个是数据(d)依赖,后者是数据(i)独立。前者应该能够抵抗 GPU 破解,而后者应该能够抵抗侧信道攻击。换句话说,Argon2d 适用于密码散列,而 Argon2i 适用于加密密钥派生。但是,无论 Argon2d 还是 Argon2i,成本参数的表现都是一样的,所以我们在这里将它们视为一个单元。
与 scrypt 一样,Argon2 也有 CPU 和 RAM 成本。但是,两者都是分开处理的。CPU 成本是通过标准迭代处理的,例如 bcrypt 或 PBKDF2,而 RAM 成本是通过专门膨胀内存来处理的。当我开始使用它时,我发现仅仅操纵迭代感觉非常像 bcrypt,但我也可以通过操纵内存来影响计算散列所需的总时间。将两者结合起来时,我发现迭代对成本的影响比 RAM 更大,但两者在计算时间上都有很大的发言权,如下表所示。与 scrypt 一样,它也有并行化成本,定义了您想要解决问题的线程数:
除了迭代次数和处理器计数成本之外,请注意 256 KiB 和 16 MiB 之间的 RAM 成本。随着我们的 RAM 膨胀,我们可以降低迭代成本。由于我们需要更多线程来处理哈希,我们可以进一步减少迭代计数。无论如何,我们试图将 0.5 秒用于交互式密码登录,将整整 5 秒用于基于密码的加密密钥派生。
结论
那么,有什么意义呢?在散列密码时,无论是将密码存储在磁盘上,还是创建加密密钥,您都应该使用专门针对此问题设计的基于密码的密码原语。你不应该使用任何类型的通用散列函数,因为它们的速度。此外,您不应该推出自己的“密钥拉伸”算法,例如递归地散列您的密码摘要和其他输出。
请记住,如果该算法是专门为处理密码而设计的,并且成本足以满足您的需求、威胁模型和对手,那么您就做得很好。真的,你不能对它们中的任何一个出错。只要避免任何不是专门围绕密码设计的算法。目标是通过肥胖获得安全。
最佳实践?按优先顺序,使用:
- scrypt
- bcrypt
- Argon2
- sha512crypt
- sha256crypt
- PBKDF2
不使用:
- MD5
- md5crypt
- UNIX crypt(3)
- SHA-1/2/3
- Skein
- BLAKE2
- 任何通用散列函数。
- 任何加密算法。
- 你自己的设计。
- 纯文本