超越 Bcrypt - Dhole 时刻

  • 2010 年:Coda Hale 撰写《How To Safely Store A Password》,强调使用 bcrypt,当时 bcrypt 是多数编程语言中存储密码的最佳答案,此文章对技术博客圈有两方面影响:让很多人认为 bcrypt 是存储密码的正确答案,也形成了技术博主为获关注推荐特定加密算法的 meme。
  • 如今(2024 年)

    • 理解 Bcrypt:Bcrypt 是密码哈希函数,不是密码 KDF 或通用加密哈希函数,使用 sane 密码存储 API 时无需考虑加盐等问题,但 bcrypt 会在 72 字符后截断(若考虑非 ASCII 密码则是字节),如 Okta 安全公告中提到的用户名 52 字符以上的情况,且人们常用 Diceware 生成的长密码短语,使用 bcrypt 会遇到截断问题。
    • “预哈希密码!”的问题:为应对 bcrypt 截断限制,很多开发者建议用通用加密哈希函数(如 SHA-256)预哈希密码,却引入了截断 NUL 字节、哈希剥离等新问题,可通过使用 HMAC 并选择合适密钥来避免哈希剥离,且使用 per-application HMAC 秘密虽增加破解难度但会使哈希不可跨应用移植。
    • 消除 Bcrypt 的隐患:避免 bcrypt 隐患的方法很简单,即预哈希用 HMAC-SHA512,确保输出为 base64 编码,再传入 PHP 的 password API,但讨论中有人提出反对意见,如认为这是“自己造密码”、72 字符以上密码罕见不值得考虑、预哈希引入拒绝服务攻击风险、存在哈希剥离风险、base64 编码降低熵等,实际上这些问题都可解决。
    • 将 Bcrypt 转化为 KDF:Bcrypt 本身不是密码 KDF,但可通过哈希 S-box 用 BLAKE2b 生成密钥包裹密钥来将其转化为 KDF,Ryan Castellucci 实现了此操作,但这只是概念证明,输出的 BLAKE2b 哈希应作为 HKDF 的输入密钥材料。
  • 总结与建议:bcrypt 仍是适合交互登录的优秀缓存硬密码哈希函数,但误用会导致应用漏洞,使用 bcrypt 时应按建议操作(HMAC-SHA-512、base64 编码、bcrypt),并给出 PHP 软件的概念验证代码,若已在生产中使用 bcrypt 应谨慎添加预哈希替代方案,WordPress 团队最终选择使用 vanilla bcrypt 而非消除其隐患,虽不是最坏结果但存在风险,希望该风险不会实际发生。
阅读 4
0 条评论