密码哈希标准:为什么 MD5 和 SHA-256 已经不够安全
如果你是一名开发者,保护用户密码是你最神圣的职责之一。明文存储密码显然是一场灾难,但许多开发者仍然犯了一个错误:使用像 MD5 甚至 SHA-256 这样“快”的加密哈希算法来存储密码。
本指南将解释为什么这些方法已经过时,以及当前行业中安全存储密码的标准是什么。
1. “快”哈希的问题
像 MD5、SHA-1 和 SHA-256 这样的算法被设计得非常快。它们旨在几毫秒内验证大文件的完整性。
虽然速度对于文件校验来说很棒,但对于密码来说却是场噩梦。现代 GPU 每秒可以计算数十亿个 SHA-256 哈希值。如果攻击者窃取了你的数据库,其中包含 SHA-256 哈希过的密码,他们可以在几小时内通过“暴力破解”猜出几乎所有简单的密码。
2. 现代慢哈希算法
为了对抗暴力破解,我们使用专门设计为计算昂贵的算法。这些是“自适应”哈希,允许你随着硬件速度的提升而增加“工作因子”。
Argon2:黄金标准
Argon2 在 2015 年赢得了密码哈希竞赛 (Password Hashing Competition),目前是 OWASP 和 NIST 推荐的标准。
- Argon2id:这是首选变体。它既能抵抗基于 GPU 的破解,也能抵抗侧信道攻击。
- 可配置:你可以调整内存占用、迭代次数和并行度,以确保在你的服务器上哈希一个密码需要大约 500 毫秒。
bcrypt:经受时间考验的经典
bcrypt 开发于 1999 年,是历史上应用最广泛、最受信任的算法之一。
- 工作因子:它使用一个“成本 (cost)”参数。每当成本增加 1,哈希密码所需的时间就会翻倍。
- 依然安全:尽管它“年事已高”,但面对现代攻击,bcrypt 依然非常安全。
scrypt:内存密集型的先驱
scrypt 是第一个引入“内存硬性 (memory hardness)”的主要算法。它需要大量 RAM 才能计算,这使得攻击者制造专门的破解硬件 (ASIC) 的成本变得极其昂贵。
3. 算法之外:盐 (Salt) 和胡椒 (Pepper)
即使有强大的算法,你仍需要额外的保护层。
盐 (Salt):击败彩虹表
盐是在哈希之前添加到密码中的随机字符串。
- 作用:如果没有盐,如果两个用户使用相同的密码(如 "123456"),他们的哈希值将完全相同。攻击者可以使用“彩虹表”(预先计算好的哈希列表)即时破解它们。
- 规则:每个用户在创建账户时都必须生成一个唯一的、随机的盐。
胡椒 (Pepper):秘密层
胡椒与盐类似,但它不存储在数据库中,而是存储在安全的配置文件或硬件安全模块 (HSM) 中。
- 作用:如果攻击者只窃取了你的数据库,但没有拿到应用程序的密钥,他们仍然无法破解哈希,因为他们不知道胡椒值。
4. 开发者最佳实践
- 绝不使用 MD5 或 SHA-1:它们在安全领域已彻底失效。
- 避免使用单纯的 SHA-256:它太快了,极易被 GPU 破解。
- 选择 Argon2id 或 bcrypt:新项目首选 Argon2id;bcrypt 是一个拥有极佳库支持的稳健替代方案。
- 调整工作因子:目标是将哈希时间设置在 250 毫秒到 500 毫秒之间。这对于用户登录来说足够快,但对于阻止攻击者来说足够慢。
- 务必加盐:大多数现代库(如 Node.js 或 Python 中的
bcrypt库)都会自动处理加盐。
总结与对比
| 算法 | 类型 | 内存硬性 | 推荐程度 |
|---|---|---|---|
| MD5 | 快哈希 | 否 | ❌ 绝不使用 |
| SHA-256 | 快哈希 | 否 | ❌ 不推荐 (用于密码) |
| PBKDF2 | 慢哈希 | 否 | ⚠️ 仅用于旧系统 |
| bcrypt | 慢哈希 | 否 | ✅ 推荐 |
| scrypt | 慢哈希 | 是 | ✅ 推荐 |
| Argon2id | 慢哈希 | 是 | 🏆 最佳 |
结论
保护密码不仅仅是选择一个随机算法,而是要使用专门设计用来抵御现代硬件强大算力的工具。通过使用带有合适盐值和工作因子的 Argon2id,你正在为用户提供当今最高水平的安全保障。