对密码进行哈希实际能保护你免受什么影响
密码哈希不是魔法。它是在你的用户表泄露那一天用于控制损害的机制。
目录
简短版本
对密码进行哈希,是在你的密码数据库被盗时保护用户。
这是它的主要工作。不是唯一细节,也不是完整的安全模型,但这是我们对密码进行哈希而不是直接存储密码的核心原因。
经过恰当哈希处理的密码很难反向还原。如果攻击者拿到你的用户表副本,他们不应立即知道 Alice 的密码是 Spring2026!。相反,他们得到的是一个已存储的哈希,需要花费时间、金钱和硬件资源去用猜测值进行测试。
这个区别很重要。密码哈希本身并不是为了让登录变得安全。它无法阻止钓鱼。它无法阻止有人拿泄露的密码去尝试你的登录表单。它也无法在登录后保护会话 cookie。它是在一个非常具体的失败发生后争取时间并降低损害:你的密码验证器存储暴露了。
如果你理解这个边界,你就能在算法、工作因子、重置、日志记录和事件响应方面做出更好的决策。
什么是密码哈希
密码哈希是将单向函数应用于密码后的输出,通常会结合一个唯一的盐,以及一个刻意较慢的密码哈希算法。
当用户创建账户时,系统大致应执行以下操作:
- 通过 HTTPS 接收密码。
- 生成一个随机且唯一的盐。
- 将密码和盐输入密码哈希函数,例如 Argon2id、bcrypt、scrypt 或 PBKDF2。
- 存储算法名称、参数、盐以及生成的哈希。
- 丢弃原始密码。
当用户稍后登录时,系统会使用提交的密码和已存储的参数重复相同的哈希过程。如果生成的哈希与已存储的哈希匹配,登录成功。
关键在于:应用程序不需要知道原始密码。它只需要验证提交的密码能否生成预期结果。
这就是为什么用可逆加密来存储密码通常是错误模型。如果你的应用程序可以解密每一个密码,那么任何偷到解密密钥的人也可以做到同样的事。密码通常应当在反向上不可验证,而不只是被隐藏起来。
哈希能保护你免受什么影响
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 值得单独审查:HttpOnly、Secure、适当的 SameSite、短生命周期的高风险会话,以及在密码变更时进行服务端失效。更广泛的隐私和浏览器环境也在持续变化,正如2026 年 cookies 发生了什么变化以及该如何应对中所述。
5. 薄弱的密码重置和账户恢复
许多账户接管并不是从密码开始的。它们从重置流程开始。
如果重置 token 可预测、生命周期过长、通过 referrer header 泄露,或被发送到已被攻陷的邮箱账户,密码哈希也救不了你。
使用高熵重置 token、较短的过期窗口、一次性使用和清晰的用户通知。由于电子邮件通常是恢复渠道,基本的域名认证也很重要。如果你的团队把 DNS 记录视为神秘仪式,可以从面向开发者的 MX、SPF、DKIM 和 DMARC 入门导览开始。
算法选择:现在该用什么
对于新应用,如果你的平台支持良好,请使用 Argon2id。它是 Password Hashing Competition 的获胜者,并且是为密码存储而设计的,包括抵抗大量依赖 GPU 的破解。
一个合理的现代优先级大致如下:
- Argon2id,用于可用的新系统。
- bcrypt,当 Argon2id 不切实际且 bcrypt 支持成熟时。
- scrypt,当内存困难配置得到良好支持时。
- 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 -->
诚实的心智模型
理解密码哈希的最佳方式是:
哈希不会在用户输入密码时保护密码。它不会在用户登录后保护账户。它不会保护那些在全网复用密码的用户。
它保护的是已存储的验证器。
这听起来范围很窄,但极其重要。数据库会泄露。备份会泄露。预发环境会被复制。供应商会获得他们不该拥有的访问权限。旧导出文件会在对象存储中停留得比任何人记得的都更久。
当这种情况发生时,你的密码存储设计就决定了攻击者拿到的是密码,还是一个昂贵的猜测问题。
这就是对密码进行哈希实际能保护你免受的影响。