这是 Beta 探索课程,内容结构、实验步骤和示例可能会继续调整。
盐与慢哈希
一次真实的教训
账号被盗事件后,我做的第一件事是检查自己的密码存储。
结果发现:我把用户的密码明文存进了数据库。
当时我想的是「系统小,用户少,不会有人来打」。直到那天我看了安全论坛的报道——每年都有大量网站因为明文存密码被拖库,用户的密码被拿去撞库、洗库,最后变成骗子手里的「密码字典」。
明文密码是认证系统里最不能碰的雷。今天我们从头把它做对。
为什么不能只用哈希
有些人会说:不存明文,存哈希不就行了?
只做一次普通哈希(比如 MD5、SHA-256)确实比明文好,但远远不够:
- GPU 暴力破解:MD5、SHA 这类快速哈希,用 GPU 每秒能算几十亿次。一个 8 位数字密码可能几分钟就被跑完。
- 彩虹表:预先把常见密码的哈希算好存起来,拿库里的哈希一比对就能反推出明文。
- 相同密码相同哈希:两个用户都用
hunter2,哈希一模一样,攻击者一次破解,两个账号一起沦陷。
所以正确做法是两条腿走路:加盐(salt)+ 慢哈希(slow hash)。
加盐:让相同密码产生不同哈希
盐是一段随机字符串,在哈希之前拼到密码里:
password + salt -> hash
每个用户(甚至每次改密)都用独立的随机盐。这样:
- 相同密码 → 不同哈希,彩虹表失效。
- 攻击者必须对每个用户单独爆破,成本大幅上升。
慢哈希:让每次尝试都很贵
MD5、SHA 的问题是算得太快。慢哈希算法通过大量迭代(和内存消耗)把单次计算拉慢到毫秒甚至百毫秒级:
| 算法 | 特点 | 适用 |
|---|---|---|
| bcrypt | 经典慢哈希,可调 cost,抗 GPU | 最广泛 |
| scrypt | 额外消耗内存,抗专用硬件 | 对硬件攻击更强 |
| Argon2 | 现代设计,同时抗 GPU 与内存侧信道 | 新系统首选 |
用户登录是一次,慢一点没关系;攻击者爆破是几亿次,慢一点点就是天文数字的成本差距。
亲自动手看看
下面是一个(示意性的)密码哈希过程,你可以输入一个密码,观察盐和慢哈希如何配合:
密码存储:盐 + 慢哈希
明文密码
(空)绝不落库随机盐 salt
每个密码独立password_hash(bcrypt 示意)
(等待输入)仅此值入库即使数据库泄露,攻击者拿到的也只是 password_hash 和 salt。慢哈希与盐会显著提高批量撞库成本。
数据库里存什么
设计上,用户表只需要保存:
user_account
├── id
├── email / phone
├── status
└── password_hash <- 算法版本 + 盐 + 哈希值
推荐把算法版本一起存进去,例如 $2b$12$... 里的 2b 是 bcrypt 版本、12 是 cost 参数。这样将来算法升级时,可以渐进式迁移:用户登录成功后,如果检测到旧算法,就顺手用新算法重新哈希,不用一次性强制全量重置。
上线检查清单
- 密码是否使用 bcrypt / scrypt / Argon2 等慢哈希 + 独立盐?
- 数据库是否只保存
password_hash,绝不保存明文或可逆加密? - 是否保存算法版本号,支持渐进式升级?
- 日志、报错、接口响应里是否有可能泄露密码或哈希?
把密码这关守住,认证系统才算有了像样的地基。下一节看登录流程本身。
发现内容有问题?
告诉我们哪里需要改进。
