用户密码应该怎么存?为什么 MD5 加盐还不够?
下面是一段教学用的模拟面试。
🧑💻 面试官:用户密码应该怎么存?
🙋♂️ 我:不能存明文,要加盐后保存哈希。
🧑💻 面试官:MD5 加随机盐,就够了吗?
🙋♂️ 我:不够,快速哈希会让攻击者很快尝试大量密码。
🧑💻 面试官:换成 SHA-256 呢?盐要藏起来吗?以后提高计算成本,已有用户怎么迁移?
密码存储要抵抗的是「数据库泄露后的离线猜测」。随机盐和足够昂贵的密码哈希,解决不同问题。
面试速答(60 秒版)
用户密码不应该保存明文,也不应该只做 MD5 或 SHA-256。它们计算很快,即使加盐,攻击者仍能高效尝试大量候选。
新系统应优先评估 Argon2id 等专门的密码哈希方案,根据环境选择参数,并使用成熟库生成独立随机盐。
盐不需要保密,可以与哈希、算法和参数一起保存。它主要防止相同密码直接得到相同结果,以及大范围复用预计算。
登录时用库的验证函数检查;发现旧算法或参数过低,可以在成功登录后重新计算并升级。同时配合登录限速、泄露密码检查和多因素认证。安全哈希不是弱密码的万能补救。

图:攻击者一侧表示拿到泄露记录后的离线猜测,不是本文声称发生过的数据库泄露流程。
知识点详解:为什么“加盐 MD5”仍然不够?
在线限制,挡不住离线猜密码
假设数据库泄露,攻击者拿到密码哈希和盐。他不再需要调用登录接口,可以在自己的机器上尝试候选密码。
接口每秒只允许几次请求,不能限制这类离线计算。存储算法必须增加每次猜测的成本。
MD5、SHA-256 等通用快速哈希,目标不是让密码猜测变慢。换更长摘要,不自动解决快速猜测。
盐让每个密码独立,成本参数让猜测昂贵
独立随机盐使相同密码得到不同结果,让预计算不能直接通用于所有用户。它不是秘密,不需要通过藏盐维持安全。
密码哈希的计算和内存成本,则提高每次验证或猜测需要付出的资源。Argon2id 的参数定义可核对 RFC 9106。
两者应一起使用。只有盐没有足够成本,仍容易猜测;只有慢算法却采用错误盐方案,也会失去重要防护。
参数不是越大越好
成本越高,正常登录也越耗资源。假设登录高峰有很多验证,参数不合理,认证系统本身可能先被拖垮。
在目标机器上测量验证耗时、内存和并发能力,选择不低于相应安全建议且可承受的参数,并设置认证限速。
截至 2026-10-07,OWASP 密码存储指南 推荐新系统优先考虑 Argon2id,不可用时评估 scrypt,遗留 bcrypt 场景注意成本和输入限制。具体参数应随指南和环境复核,不能背成永远有效的数字。
保存格式,为未来升级留位置
保存算法标识、版本、成本参数、盐和哈希,或采用库的规范编码格式。不要只留一串无法判断来源的摘要。
登录时按旧记录参数验证。成功后,若算法或成本需升级,用本次输入的密码重新计算,替换旧记录。
没有原密码,无法把旧哈希直接转成新方案的正确密码哈希。长期不登录用户可能需要重置,不能声称批量改数据库就升级完成。

图:成功验证后,再升级密码记录。
还需要保护什么?
密码不要进入普通日志、分析平台或错误上报。传输使用安全连接,验证交给成熟库,不自己拼凑比较逻辑。
如果使用 pepper,应与数据库分开管理,安排轮换和恢复。它是额外防护,不替代盐与密码哈希,也增加密钥管理责任。
本题不提供未经验证的 TS/Python 加密库拼装代码。实际实现应选定具体库与参数后,分别按官方 API 测试。
面试官继续追问
密码能加密保存吗?
普通认证只需要验证,不需要恢复原文。可逆加密增加密钥泄露后还原全部密码的风险,通常不应替代密码哈希。
哈希后就不用限速?
仍然需要。在线攻击与认证资源耗尽,需单独防护。
为什么不对旧哈希再套新算法?
这不是对原密码采用新方案,验证过程和安全性质都改变了。正常迁移在成功登录或重置时取得原密码输入。
面试速记卡
- 威胁:数据库泄露后的离线猜测。
- 盐:独立随机,不必保密。
- 密码哈希:专用慢算法并校准成本。
- 迁移:旧验证成功后按新方案重新计算。
- 完整防护:安全传输、限速、日志保护与密钥管理。
公司面试真题
这道题暂未收录可核验的公司真题来源。你可以先阅读本文解析,或浏览已收录的公司面试真题。
浏览公司面试真题 →