我必须将密码存储在不提供任何推荐用于散列密码的算法(例如 bcrypt、scrypt 或 PBKDF2)的系统上。在不使用之前使用不合适的算法(例如 SHA-1 或 SHA-2 系列)会更好吗?
附加信息:我将是唯一一个在该系统上存储密码的人,因此我可以 1)保证密码不会被使用两次,2)确保密码具有非常高的熵。
我必须将密码存储在不提供任何推荐用于散列密码的算法(例如 bcrypt、scrypt 或 PBKDF2)的系统上。在不使用之前使用不合适的算法(例如 SHA-1 或 SHA-2 系列)会更好吗?
附加信息:我将是唯一一个在该系统上存储密码的人,因此我可以 1)保证密码不会被使用两次,2)确保密码具有非常高的熵。
您问题的真正答案是,如果您无法正确保护密码,请不要将密码存储在那里。
“系统不支持正确的密码散列算法”不是损害用户安全的有效借口。要么找到一种使用强大且正确配置的算法正确散列密码的方法,要么不存储它们。
但是要直接回答您的问题:是的,有总比没有好。SHA-1 或 SHA-2 绝对是对明文的改进。
回答“那么密码应该去哪里?” 问题 - 根据措辞,我假设软件有一个新功能/请求,需要系统存储密码。
如果系统不能正确和安全地存储密码(按照现代安全标准),那么我(个人)会拒绝该请求。由于遗留系统限制或安抚业务用例而故意实施较差的安全性是不好的安全实践。我会通知提出请求的人我有两个可行的选择:
我们必须彻底改造系统,使其支持现代安全实践(例如正确的加密密码散列)。
我根本无法将新功能/请求添加到现有系统,因为它会损害安全性。
是的,弱加密哈希总比没有哈希好。
如果您的密码数据库被盗,哈希密码的原因是:
1 是一场军备竞赛:你想使用更大、更慢的哈希函数,这样攻击者会花费更多的电力来执行暴力破解哈希。SHA-1/2 的单次迭代总比没有哈希好,因为攻击者必须进行一些破解。salted SHA-1/2的单次迭代将迫使他们在无法使用预先计算的彩虹表的情况下进行一些破解。使用更高工作因子的更高级的密码哈希(argon2、bcrypt 或旧的 scrypt、pbkdf2)将进一步提高破解的电力成本。
使用任何加盐的密码散列(即使是不推荐用于密码的散列,如 SHA-1、SHA-2)都会带来 2 的好处。
在其核心,PBKDF2 只是迭代和加盐的 SHA-1/2,所以我会为 PBKDF2 实现做一些谷歌搜索,并围绕 SHA-1/2 构建你的包装器来模拟它(或者更好,一路走下去并实际实现PBKDF2)。
正如其他人所说,尽可能在您的平台语言中找到一个强大的独立实现。
但是,如果您不能使用强哈希......那么您绝对应该仍然对用户的密码进行哈希处理,即使使用的是弱哈希 - 因为即使是弱哈希也会保护强密码。
当 hashcat 等工具每秒可以猜测数十亿个密码时,弱哈希将无法保护弱密码。但是,如果您的一个具有安全意识的用户选择了一个不可破解的密码,例如“SDXBZsRVBKVnXznpLTBMIKhTX”或“afferently-imitatee-snowmelt-heirdom-leeching”……那么即使是像 SHA1 这样的弱散列仍然会使该密码无法被暴力破解攻击。
即使使用弱哈希,您至少可以让精明的用户保护自己,即使您的系统很弱。(如果你有能力拉伸——连续数千次使用相同的算法——这也会减慢攻击者的速度)。
但是您的精明用户不会重用密码,因此即使这也是有限的价值(除了“半精明”用户选择了足够强的密码以抵抗暴力破解,但可能在其他地方重用该密码或使用人类可解析的密码选择方案......所以即使使用弱哈希也可以保护这些用户)。
对所有用户来说,最好的选择是找到一种使用强哈希的方法。
[编辑:OP 已更新问题以表明他们是系统的唯一用户,并且可以设置任意随机密码(这对我来说似乎很奇怪;我不知道有这样一个奇怪组合的系统“我是唯一的用户”、“我只能从几个散列算法中挑选”和“所有的散列算法都很弱”)。无论哪种方式,这使我对保护普通用户的弱密码的担忧与这个特定问题无关。其他答案应该在他们的答案开始时澄清他们给出的建议通常会很糟糕,并且只适用于这个非常具体的问题。]
[编辑 2:请注意,“弱哈希”与“坏哈希”不同。一个坏散列的例子是解密(因为它会将密码截断为 8 个字符)。因此,即使您的密码很强大,一个 8 字符的解密哈希也可以在产消级(6 GPU)破解系统上在 10 天或更短的时间内被暴力破解。]
在这种特殊情况下,SHA-2 与 PBKDF/scrypt/bcrypt 一样安全。迭代是不必要且浪费的。生成高熵(256 位)密码,忘记 KDF 甚至盐。
事实上,使用 SHA-2 比使用 PBKDF 更有意义,因为该算法没有现有的平台实现,并且“滚动你自己的加密”是一个禁忌。
这是一个相当强烈的声明。问题的本质是 OP 可以完全控制他存储在系统上的密码,并且知道他将是唯一一个在系统上存储密码的人。
Signal在其双棘轮算法中使用了一种称为 HKDF 的结构,该结构本质上仅将散列算法应用于一次迭代。
为什么这样可以?
密码 KDF 的存在正是一个原因:低熵密码。大多数人不会记得他们脑海中的 CSRNG 生成的 128 位或 256 位密钥。我敢冒险,大多数人最多可以记住一个 40 位的随机字符串足够长的时间来使用它。
几乎所有的散列函数(甚至是 MD4、MD5 和 SHA-1,但既然你说你在平台上有 SHA-2,让我们在这里保持安全)都有一个称为原像电阻的特性,这对你的应用程序来说已经足够好了,因为你控制存储在系统上的所有密码。这意味着,在给定哈希的情况下,生成哈希到相同哈希的东西在计算上是不可行的。
稍微简化一下,为这些哈希函数之一生成原像的最实用方法是不断尝试输入。有一些攻击的例子比蛮力稍微好一点,但它们是完全不可行的。现在,如果您选择放入散列函数的密码/密钥只有 16 位熵,则此阻力属性不会对您有太大影响,因为攻击者将能够尝试您可能输入的所有 65536 个不同的输入。
为什么控制密码很重要
少考虑您作为“密码”输入的内容,而多考虑密码密钥。只要您对熵大于或等于您选择的散列函数输出的位数(对于 SHA-256,即 256 位)的散列键进行散列,那么只需运行一次散列迭代就可以了在它上面以防止密钥提取。哈希的原像抗性与密钥的高熵相结合意味着攻击者根本无法猜测您输入的值。无需数千次迭代或与业务方争论或推理抽象攻击不- 很容易接受的老板。当您必须猜测散列函数的 256 位输入时,FPGA/ASIC 阻力是一个有争议的问题。
建议
使用硬件 RNG 或 CSPRNG 生成您的“密码”,并立即销毁任何可用于恢复任何内部状态和密码的种子材料。确保这些是 256 位长。如果您的系统不支持非字母数字或非 ASCII 密码,则在生成 256 位后,将其编码为 base-64 或 base-26 或二进制(如果您愿意)。(这意味着您提交的实际密码将超过 32 个字节,但这对熵没有多大帮助。)
将这些密码视为加密密钥——理想情况下,它们将存储在硬件令牌中。硬件密码管理器可以很好地用于此目的。将 SHA-256 散列存储在您的系统中,并通过散列和比较来验证密码。即使哈希匹配,您也应该拒绝所有与您的生成标准不匹配的尝试密码(例如用户选择的密码 < 256 位),并且您不应允许任何人设置不是来自CSRNG。这应该记录在案。更好的是,如果您在生成的密码末尾添加一些模糊的校验和字节,在平台允许您设置密码之前由平台验证。这应该会阻止除最可笑的无能者之外的所有人在您的平台上放置任何比加密密钥更少的东西。
公钥密码学
你的用例有点奇怪。大多数地方都保留密码,因为设置 PKI 和与最终用户打交道非常困难,尤其是像公钥这样的东西。由于您似乎是唯一一个将密码输入系统的人,因此将 Ed25519 或 ECDSA 公钥存储在系统上并在那里编写实现质询-响应协议的代码可能更有意义(一个简单的方法是签署这个 256-比特价值——但请注意不要重复使用这些密钥,因为有人可能会欺骗你签署你的比特币或犯罪供词)。将在此处与您的系统交互并维护私钥的设备可能具有强大的公钥加密实现,并且可以毫无问题地进行身份验证。你的“自己动手”