密码并不是"存储"的
任何正规的 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 确认酱上检查网络信息一样,也请定期审视你的密码管理状况。