为什么不能只用 SHA
SHA 速度快,攻击者可以用彩虹表或字典对海量密码瞬间反查。给密码加同一个盐只能挡彩虹表,挡不住针对单个哈希的暴力破解。
正确做法
- 每个用户独立加盐:盐随机生成、随哈希一起存,不必保密但必须唯一;
- 用慢哈希算法:bcrypt / scrypt / Argon2 故意算得慢、可调成本,拖慢暴力破解;
- 提高工作因子:随着硬件变快逐步调高,但别高到影响登录体验。
泄露之后
- 强制重置:通知用户改密码,而不是「建议」;
- 加额外验证:可疑登录要求第二步验证;
- 别明文发密码:「忘记密码」应发一次性重置链接,而不是把原密码发回来(说明你存了明文)。
动手试试
实战案例:三次口令存储事故
- 用 MD5 存口令:一次脱库即被彩虹表批量还原。应使用 bcrypt / scrypt / Argon2 这类慢哈希,并设置足够的工作因子。
- 全局共用一个盐:所有口令同盐,相同口令仍得到相同摘要,攻击者可一次比对命中。盐必须每条记录独立且随机。
- 把哈希当加密用:指望“解密”回口令用于第三方登录,结果只能强制重置。哈希是单向的,需要还原的场景应改用可逆加密并妥善管理密钥。
常见问题(FAQ)
盐需要保密吗?不需要,盐可随摘要一起存库,它的作用是让相同口令产生不同摘要。工作因子怎么选?以单次验证 100–300 毫秒为参考,并随硬件升级逐步提高。要不要“先加密再哈希”?可选,但前提是密钥管理可靠;多数场景选用参数合理的 Argon2 已足够。怎么平滑升级算法?登录成功时先用旧算法验证,再用新算法重新写入摘要。
迁移与巡检:把算法升级做成常规动作
很多团队的口令存储不是一次设计出来的,而是在多年演进中逐步替换的,因此必须有一套可以长期执行的迁移与巡检机制,否则算法会随着时间推移越来越弱,而没人意识到。
- 识别旧格式:在数据库中让摘要字段带上算法标识前缀,例如注明使用何种算法与参数。这样校验时可以按前缀选择对应算法,而不用靠猜或额外字段。没有前缀的历史数据应先做一次全量盘点,明确哪些是明文、哪些是弱哈希。
- 登录时静默升级:用户登录成功后,用旧算法验证通过的那一刻,顺手用新算法重新计算并覆盖存储。用户无感知,也不需要强制所有人重置口令。这个过程应打点统计,确认升级进度,避免长时间停留在两套算法并存的状态。
- 批量强制升级:对于长期不登录的账号,可以结合风险策略在下次登录时要求重置;若合规要求严格,应设定明确期限,到期未升级的账号降级处理并要求通过邮箱或短信重新设置。
- 参数巡检:把工作因子纳入配置管理,并在每次上线前评估一次当前硬件下的单次验证耗时。如果已经降到几十毫秒,说明参数偏弱,应上调;如果超过半秒,用户会明显感知登录变慢,需要权衡。
- 禁止明文与可逆存储:任何“为了便于找回口令而加密存储”的做法都应被明确禁止。可恢复意味着可泄露,一旦密钥与密文同时失守,影响面远大于哈希。
此外还要注意与口令相邻的几个环节:重置口令的令牌必须一次性且短效;客服流程不能成为绕过验证的后门;管理员不应具备查看用户口令的能力——这些细节往往比算法本身更容易被突破。
口令之外的关联风险
凭证填充攻击会把其它站点泄露的口令拿来尝试登录,因此除了哈希算法,还应关注登录接口的限速、异常登录检测与多因素认证。把这几件事配合起来,单项措施即使被绕过,整体防线仍然成立。