密码并不是"存储"的

任何正规的 Web 服务都不会原样存储你的密码。存储的是将密码通过单向函数(哈希函数)生成的"哈希值"。从哈希值无法还原出原始密码。

登录时,服务器对输入的密码进行哈希运算,并与存储的哈希值进行比较。如果匹配,则认证成功。密码本身从未被记录在服务器的存储中,哪怕一瞬间也没有。

那么为什么要采用这种迂回的方式呢?这是为了在数据库被攻破时将损害降到最低。

明文存储的恐怖 - 真实事件

当以明文(原始字符串)存储密码的公司遭遇数据泄露时,每个用户的密码立即落入攻击者手中。

  • Adobe(2013 年):1.53 亿账户记录泄露。密码虽然加密但使用了可逆加密(3DES-ECB)而非哈希,意味着相同的密码产生相同的密文。通过频率分析,大量密码被破解
  • RockYou(2009 年):3200 万个密码以明文泄露。这些数据至今仍被广泛用作密码破解的字典
  • Facebook(2019 年):数亿个密码被发现以明文记录在内部系统中。虽然没有外部泄露,但公司内部任何人都可以查看

如果密码经过哈希处理,攻击者从数据库泄露中获得的只是哈希值。要发现原始密码,需要"逆向"哈希值,但使用适当的哈希函数,这在计算上是不可行的。

哈希函数的进化 - 从 MD5 到 bcrypt

MD5 和 SHA-1 - 太快反而不安全

早期的密码哈希使用 MD5 或 SHA-1。然而,这些函数的设计目标是"快速计算",这对密码哈希来说实际上是适得其反的。现代 GPU 每秒可以计算数百亿次 MD5 哈希,通过暴力破解可以在几秒内破解短密码。

盐值 - 相同密码也产生不同哈希

盐值是在密码哈希前添加的随机字符串。即使是相同的密码"password123",由于每个用户添加了不同的盐值,也会产生不同的哈希值。这使得使用预计算哈希表(彩虹表)的攻击失效。

bcrypt - 故意设计得很慢的哈希函数

bcrypt(1999 年)是专门为密码哈希设计的函数。其关键特点是"故意很慢"。成本因子(工作因子)参数允许调整计算成本,可以随着硬件性能的提升而增加。

如果 bcrypt 配置为每次哈希计算需要 100 毫秒,合法登录(一次计算)几乎不受影响,但暴力攻击(数十亿次计算)变得几乎不可能。

Argon2 - 当前最先进的方案

Argon2 在 2015 年的密码哈希竞赛中获胜,不仅可以调整计算时间,还可以调整内存使用量。它比 bcrypt 对基于 GPU 的并行攻击具有更高的抵抗力。

如果"忘记密码"给你发回了原始密码,那就是危险信号

如果某个服务在密码重置时通过邮件发送了你的原始密码,那么该服务是以明文(或可逆加密)存储密码的。如果密码经过哈希处理,恢复原始密码是不可能的。

正确的实现是在密码重置流程中设置"新密码"。能告诉你原始密码的服务,证明它不懂安全基础。

总结

密码哈希是建立在"数据库会被攻破"这一前提上的防御。哈希函数从 MD5 时代经过 bcrypt 发展到 Argon2,不断对抗攻击者日益增长的计算能力。

作为用户,你能做的是为每个服务使用不同的强密码,启用双因素认证,如果可能的话迁移到 Passkey。就像你在 IP 确认酱上检查网络信息一样,也请定期审视你的密码管理状况。

本文相关术语

哈希函数 将任意长度的数据转换为固定长度值的单向函数。用于密码存储和数据完整性验证。 加密 将数据转换为第三方无法读取的格式的技术。与哈希不同,可以解密(逆向)。 双因素认证 在密码之外使用第二个因素进行认证的机制。减轻密码泄露的损害。 Passkey 无密码认证方式。基于公钥密码学,服务器端不存储秘密信息。 IP 地址 网络设备标识符。在密码泄露事件中用于识别未授权访问的来源。