Privacy & Security

对密码进行哈希实际能保护你免受什么影响

密码哈希不是魔法。它是在你的用户表泄露那一天用于控制损害的机制。

The Wux Webtools Team The Wux Webtools Team 10 分钟阅读 人工智能辅助,人工审核
Illustration of a password being transformed into a protected hash before storage in a database.
目录
  1. 简短版本
  2. 什么是密码哈希
  3. 哈希能保护你免受什么影响
  4. 1. 数据库泄露后的即时密码披露
  5. 2. 针对整个用户群的批量攻击
  6. 3. 快速离线猜测
  7. 哈希不能保护你免受什么影响
  8. 1. 钓鱼
  9. 2. 凭据填充
  10. 3. 被日志或分析捕获的密码
  11. 4. 糟糕的会话安全
  12. 5. 薄弱的密码重置和账户恢复
  13. 算法选择:现在该用什么
  14. 成本因子不是一劳永逸的
  15. Peppers:有用,但不是替代品
  16. 运营检查清单
  17. 诚实的心智模型

简短版本

对密码进行哈希,是在你的密码数据库被盗时保护用户。

这是它的主要工作。不是唯一细节,也不是完整的安全模型,但这是我们对密码进行哈希而不是直接存储密码的核心原因。

经过恰当哈希处理的密码很难反向还原。如果攻击者拿到你的用户表副本,他们不应立即知道 Alice 的密码是 Spring2026!。相反,他们得到的是一个已存储的哈希,需要花费时间、金钱和硬件资源去用猜测值进行测试。

这个区别很重要。密码哈希本身并不是为了让登录变得安全。它无法阻止钓鱼。它无法阻止有人拿泄露的密码去尝试你的登录表单。它也无法在登录后保护会话 cookie。它是在一个非常具体的失败发生后争取时间并降低损害:你的密码验证器存储暴露了。

如果你理解这个边界,你就能在算法、工作因子、重置、日志记录和事件响应方面做出更好的决策。

什么是密码哈希

密码哈希是将单向函数应用于密码后的输出,通常会结合一个唯一的盐,以及一个刻意较慢的密码哈希算法。

当用户创建账户时,系统大致应执行以下操作:

  1. 通过 HTTPS 接收密码。
  2. 生成一个随机且唯一的盐。
  3. 将密码和盐输入密码哈希函数,例如 Argon2id、bcrypt、scrypt 或 PBKDF2。
  4. 存储算法名称、参数、盐以及生成的哈希。
  5. 丢弃原始密码。

当用户稍后登录时,系统会使用提交的密码和已存储的参数重复相同的哈希过程。如果生成的哈希与已存储的哈希匹配,登录成功。

关键在于:应用程序不需要知道原始密码。它只需要验证提交的密码能否生成预期结果。

这就是为什么用可逆加密来存储密码通常是错误模型。如果你的应用程序可以解密每一个密码,那么任何偷到解密密钥的人也可以做到同样的事。密码通常应当在反向上不可验证,而不只是被隐藏起来。

哈希能保护你免受什么影响

1. 数据库泄露后的即时密码披露

如果攻击者窃取了一个包含明文密码的数据库,损害会立即发生。每一个密码都会暴露。用户不仅在你的网站上有风险,在他们复用该密码的任何地方也有风险。

如果数据库包含经过良好哈希处理的密码,攻击者就需要做更多工作。他们必须猜测候选密码,用正确的盐和参数对每个猜测值进行哈希,然后比较结果。

对于弱密码,这仍然可能很快。对于强且唯一的密码,这可能变得不切实际。

哈希会把灾难性的披露变成一场竞赛:用户能否重置密码,你能否在攻击者破解大量密码之前控制事件?

这并不完美。它仍然是一次泄露。但这是一种好得多的失败模式。

2. 针对整个用户群的批量攻击

盐是密码存储的关键组成部分,因为它可以防止攻击者利用预计算表高效地同时攻击许多用户。

盐不是秘密。它与哈希一起存储。它的作用是提供唯一性。

如果两个用户选择了相同的密码,唯一的盐会确保他们存储的哈希不同。这可以防止攻击者一眼看出许多用户共享同一个密码。它也可以防止经典的彩虹表攻击,即攻击者使用巨大的、预先计算好的密码到哈希映射列表。

没有盐时,一个被破解的哈希可能会暴露所有使用相同密码的用户。有了盐,每个密码猜测都必须针对每个用户分别测试。

3. 快速离线猜测

一旦攻击者拿到密码数据库,他们就可以离线猜测。这意味着你的登录速率限制、CAPTCHA、IP 封禁和监控都不再重要。攻击者可以在自己的硬件上测试猜测值。

这就是算法选择发挥作用的地方。

SHA-256 和 SHA-512 这样的通用哈希被设计为快速运行。这对文件完整性和数字签名来说是好事。对密码存储来说则是坏事。

密码哈希算法被设计为缓慢、可调,有时还具备内存困难特性。Argon2id、bcrypt、scrypt 和 PBKDF2 都允许你调整成本参数,使每次猜测都需要有意义的时间。

Argon2id 被广泛推荐用于新系统,因为它可以配置为同时需要 CPU 时间和内存,从而让大规模 GPU 破解成本更高。bcrypt 仍然常见,并且在配置良好时可以接受,尽管它存在密码长度处理等限制。PBKDF2 仍在一些由合规驱动的环境中使用,尤其是在需要 FIPS 验证组件的地方。

原则很简单:让合法登录保持可接受的速度,同时让数十亿次猜测变得昂贵。

哈希不能保护你免受什么影响

1. 钓鱼

如果用户把密码输入到假的登录页面,你服务器上的哈希不会有帮助。攻击者会在你的系统看到密码之前就收到它。

这里的防御措施不同:多因素认证、passkeys、用户教育、域名卫生、抗钓鱼认证以及谨慎的密码重置流程。

密码哈希是存储密钥的后备保护。它不是防止用户被诱骗交出这些密钥的防御手段。

2. 凭据填充

凭据填充是指攻击者拿到从某个服务泄露的用户名和密码组合,然后在另一个服务上尝试。

你的密码哈希可能非常优秀,但如果用户复用密码,凭据填充仍然可能成功。

这是针对你的登录表单的在线攻击,而不是针对你的数据库的离线攻击。你需要速率限制、异常检测、已泄露密码检查、MFA,以及不会轻易制造拒绝服务机会的合理锁定策略。

同样的实践思路也适用于任何暴露的表单。如果你正在审查认证攻击面,值得阅读为什么你的联系表单是你最大的垃圾信息负担;机制不同,但教训相似:公共输入需要滥用控制,而不只是干净的后端代码。

3. 被日志或分析捕获的密码

只有在明文密码被快速丢弃且从未复制到其他地方时,哈希才有帮助。

常见失败包括:

  • 在失败的登录尝试中记录完整请求体。
  • 将密码发送到错误监控工具。
  • 在会话回放产品中捕获密码字段。
  • 在设计糟糕的重置或迁移流程中把凭据包含在 URL 中。
  • 在导入期间临时存储明文密码。

这些错误会完全绕过密码哈希。如果明文落入日志、备份、数据仓库或第三方工具,你的哈希函数就无关紧要。

把密码字段视为有毒数据。在记录日志之前对其进行脱敏。将其排除在分析之外。不要让它们出现在 URL 中。限制能够访问生产环境追踪信息的人员。

4. 糟糕的会话安全

登录后,用户的浏览器通常会收到一个会话 cookie 或 token。如果该 token 被盗,攻击者可能根本不需要密码。

密码哈希无法防御跨站脚本、不安全的 cookie、会话固定、弱 token 生成或过长的会话生命周期。

会话 cookie 值得单独审查:HttpOnlySecure、适当的 SameSite、短生命周期的高风险会话,以及在密码变更时进行服务端失效。更广泛的隐私和浏览器环境也在持续变化,正如2026 年 cookies 发生了什么变化以及该如何应对中所述。

5. 薄弱的密码重置和账户恢复

许多账户接管并不是从密码开始的。它们从重置流程开始。

如果重置 token 可预测、生命周期过长、通过 referrer header 泄露,或被发送到已被攻陷的邮箱账户,密码哈希也救不了你。

使用高熵重置 token、较短的过期窗口、一次性使用和清晰的用户通知。由于电子邮件通常是恢复渠道,基本的域名认证也很重要。如果你的团队把 DNS 记录视为神秘仪式,可以从面向开发者的 MX、SPF、DKIM 和 DMARC 入门导览开始。

算法选择:现在该用什么

对于新应用,如果你的平台支持良好,请使用 Argon2id。它是 Password Hashing Competition 的获胜者,并且是为密码存储而设计的,包括抵抗大量依赖 GPU 的破解。

一个合理的现代优先级大致如下:

  1. Argon2id,用于可用的新系统。
  2. bcrypt,当 Argon2id 不切实际且 bcrypt 支持成熟时。
  3. scrypt,当内存困难配置得到良好支持时。
  4. PBKDF2,当平台或合规约束要求时。

避免使用普通的 SHA-256、SHA-512、MD5,或像 sha256(password + salt) 这样的自制组合。快速哈希不是密码存储函数。聪明的自定义构造往往比乏味的标准方案更糟。

也不要围绕算法细节发明自己的密码策略。如果 12 条密码组成规则清单把用户推向可预测模式,用户并不会从中受益。更长且唯一的密码、密码管理器、已泄露密码筛查和 MFA 通常更重要。

成本因子不是一劳永逸的

密码哈希有参数。Argon2id 有内存、迭代次数和并行度。bcrypt 有成本因子。PBKDF2 有迭代次数。

这些值应根据你的生产环境来选择。太低,攻击者就能廉价猜测。太高,你的登录系统就会变慢,或容易受到拒绝服务攻击。

一个实用目标通常是在你的实际服务器上,每次密码验证耗时几十到几百毫秒不等,具体取决于流量和风险。高安全系统可能会选择更高的值。面向消费者的大规模系统可能需要谨慎的容量规划。

不要从五年前的博客文章中照搬成本因子。硬件会变化。库会变化。你的流量也会变化。

定期审查参数,并规划重新哈希。常见模式是把算法和参数与每个哈希一起存储。在成功登录时,如果存储的参数已经过时,就用新的配置对提交的密码重新哈希,并更新记录。

Peppers:有用,但不是替代品

pepper 是一个加入密码哈希过程的秘密值,并且与数据库分开存储,通常存放在 secrets manager 或 hardware security module 中。

与盐不同,pepper 必须保持秘密。

如果数据库泄露但应用程序秘密没有泄露,pepper 可以降低损害。它们在具备良好密钥管理的成熟环境中最有用。如果同一个攻击者能够同时窃取数据库和应用程序配置,它们的作用就小得多。

如果你使用 pepper,请仔细规划轮换。根据设计不同,轮换它可能需要用户重新登录或重置密码。pepper 是额外的一层,而不是削弱底层哈希设置的理由。

运营检查清单

如果你负责一个真实系统,实用检查清单很短:

  • 只使用标准密码哈希算法存储密码。
  • 为每个密码使用唯一的随机盐。
  • 新构建优先使用 Argon2id。
  • 在类似生产环境的硬件上调优成本参数。
  • 将算法和参数与每个哈希一起存储。
  • 当参数过时时,在登录时重新哈希。
  • 永远不要记录密码,也不要将其发送到分析工具。
  • 在提交凭据的所有地方使用 TLS。
  • 在风险值得时添加 MFA 或 passkeys。
  • 像保护登录流程一样认真保护重置流程。
  • 为强制重置和用户通知制定事件计划。

密码哈希并不光鲜。它是管道工程。但正是这种管道工程决定了一次泄露会成为痛苦的事件,还是波及所有用户的灾难。

<!-- tool-cta:start -->

💡 试试看: 使用 Hash Generator 查看相同输入如何映射到不同算法,从而具体呈现快速哈希与密码级哈希之间的差异。

<!-- tool-cta:end -->

诚实的心智模型

理解密码哈希的最佳方式是:

哈希不会在用户输入密码时保护密码。它不会在用户登录后保护账户。它不会保护那些在全网复用密码的用户。

它保护的是已存储的验证器。

这听起来范围很窄,但极其重要。数据库会泄露。备份会泄露。预发环境会被复制。供应商会获得他们不该拥有的访问权限。旧导出文件会在对象存储中停留得比任何人记得的都更久。

当这种情况发生时,你的密码存储设计就决定了攻击者拿到的是密码,还是一个昂贵的猜测问题。

这就是对密码进行哈希实际能保护你免受的影响。

常见问题

如果我加了盐,SHA-256 足够用于密码存储吗?
不够。盐是必要的,但 SHA-256 仍然太快。密码存储需要缓慢且可调的函数,例如 Argon2id、bcrypt、scrypt 或 PBKDF2。
密码应该加密而不是哈希吗?
通常不应该。加密是可逆的,这意味着被盗的密钥可以暴露每一个密码。密码通常应存储为不可逆的哈希。
盐和 pepper 有什么区别?
盐是一个唯一的、非秘密的值,与每个密码哈希一起存储。pepper 是一个共享秘密,与数据库分开存储。盐是必需的;pepper 是可选的,并且在运营上更复杂。
密码哈希能防御凭据填充吗?
不能。凭据填充是使用从其他服务泄露的密码进行的在线攻击。你需要速率限制、已泄露密码检查、MFA 和监控来降低这种风险。
我需要重新哈希旧密码吗?
通常需要。将哈希参数与每条密码记录一起存储,然后在算法或成本设置过时时,在成功登录后重新哈希。

来源与进一步阅读

  1. OWASP Password Storage Cheat Sheet
  2. NIST Special Publication 800-63B: Digital Identity Guidelines
  3. RFC 9106: Argon2 Memory-Hard Function for Password Hashing and Proof-of-Work Applications
  4. Have I Been Pwned: Pwned Passwords
关于作者
The Wux Webtools Team

最后更新:

继续阅读