我错了吗?在 bcrypt 之前使用 SHA256 散列密码是否不会降低安全性,即使在理论上也是如此?
bcrypt 有一个 184 位的输出哈希。将更多熵位作为输入并不会改变可能输出的数量被限制在该值之下。
256 位 > 184 位,因此我看不出安全性会如何降低。
您可能会问为什么输入更宽?(72 字节与 23 字节)
它的长度与熵位无关,单词和表情符号可以使用更多的长度/字节,当这些用作“字母”的单位来组成密码时,您可以了解熵位的数量如何不限于单个字节/字符(这是误解似乎集中的地方)。
SHA-256 允许您将该表示压缩到 bcrypt 接受的输入大小,它仍然可以保持原始输入的熵。
更多细节
256 位足够安全/防御,并且比 192 位输出更多
我在这里看到关于 SHA-256 是 32 个字节或64 个字符作为字符串的讨论(假设十六进制编码,16 值字符集 0-9,af),无论你怎么看,你仍然有 256 位表示。
这已经是一个不切实际的攻击量(并不是说这些哈希会受到攻击,因为可以肯定实际密码的熵少于 256 位)。
您还将获得 184 位的输出哈希(在 Radix-64 编码为 31 个字符之前,8 位被截断),因此任何关于减少输入的担忧都没有实际意义,无论如何,您很快就会在输出上遇到冲突。
另请注意,虽然限制为 72 个字节,但某些实现可能会将输入截断/限制为 55 个字符长度的字符串(包括空终止符字节时为 56 个)。
因此,如果您没有将 SHA-256 哈希的 32 个字节传递给 bcrypt,而是将其作为十六进制字符串提供给 bcrypt,则您可能希望使用 base64 编码,它将 32 个字节表示为 44 个字符而不是 64 个字符。
使用散列来限制输入的长度还可以避免实现错误(修复 OpenBSD 2014、NodeJS 2020),其中超过 255 字节的密码会溢出 8 位字符串长度,这可能会将密码视为只有几个字符长。
密码熵的组成不限于单个字母数字/ASCII 字符
密码熵不仅仅是通过单个字符/字节来衡量,这在其他关于 95 个 ASCII 值字符集的答案中是一个共同的焦点。您可以使用单词(例如7776 个单词的 EFF diceware 列表)替换密码组合中的单个 ASCII 值,或者在这种情况下使用密码短语。
这些当然长度更长,因此字节更长,如果每个单词平均 10 个字符,那么在从其他单词到 bcrypt 的任何额外熵丢失之前,您只能容纳 7 个单词。那只有大约 90 位 ( log2(7776^7)
)。
密码还不必局限于有限字母表中的单词。外语甚至表情符号都可以是有效的输入,但对于单个视觉字形,它们可能使用多个字节。
单个字形(“字符”)在视觉上可以用多个字节表示,尤其是表情符号
您可以拥有一个使用17 个字节的表情符号,例如:(🕵🏼♀️ 侦探 + 肤色 + 性别组合),用 unicode 中的 5 个代码点表示:0x1f575 0x1f3fb 0x200d 0x2640 0xfe0f
. 这些表情符号由一系列其他基本表情符号和一些不可见的修饰符(如0x200d
ZWJ 和0xfe0f
VS16)组成。
单个字形,多个代码点(每个 UTF-8 编码代码点的字节数各不相同)。有些表情符号仍然使用更多字节,但表情符号的整体熵位并没有那么高,无法证明字节成本是合理的,就像使用 bcrypt 一样。一个典型的表情符号(不涉及任何序列)可能使用 3-4 个字节。
TL;DR:SHA-256 允许避免长度限制,否则熵会丢失
因此,输入密码的 SHA-256 哈希可以解决长度问题。当前的表情符号约为3,521(截至 2020 年 9 月 Unicode 13.1),21 个表情符号将适合 256 位熵 ( log2(3521^21) = ~247
),但可以很好地使用超过 72 个字节的大小,根据表情符号的选择可能超过 500 个字节。使用 SHA-256 哈希可确保您不必担心用户密码的字节长度。
👩👩👧👦👩👩👧👦👩👩👧👦🕵🏼♀️
(92 字节)与👩👩👧👦👩👩👧👦👩👩👧👦❤️
(81 字节),前 3 个家族表情符号都使用 75 个字节(每个 25 个)。如果您使用 bcrypt 输出具有相同盐的散列,它们都将忽略第 4 个字形,从而产生相同的散列。