起诉书:两起真实的大规模数据泄露
案例一:Adobe,2013 年,1.53 亿用户。黑客窃取了 Adobe 的用户数据库。研究人员发现,Adobe 用 3DES 对称加密存储密码——不是哈希,是加密,意味着有对应的解密密钥。更糟糕的是,相同的密码被加密成相同的密文,通过频率分析,研究人员在几小时内还原了超过 200 万个常用密码。
案例二:LinkedIn,2012 年(2016 年才被披露),1.17 亿用户。LinkedIn 用的是 unsalted SHA-1——比 MD5 稍好,但仍然是无盐哈希。黑客把这批哈希挂到 GPU 集群上跑,3 天内破解了 90% 以上的账号密码。
这两起事故的共同点:开发者做出了错误的安全选择,最终付出代价的是用户。
技术法庭:MD5 的三宗罪
第一罪:速度太快。 MD5 是为数据完整性校验设计的,目标是极快地生成摘要。现代 GPU 每秒可以计算 200 亿次 MD5 哈希,这意味着攻击者可以在极短时间内穷举数十亿个常见密码的 MD5 值。
第二罪:彩虹表的存在。彩虹表是预先计算好的"密码 → MD5"映射数据库,涵盖了互联网上 99% 的常见密码。查一下,就知道 5f4dcc3b5aa765d61d8327deb882cf99 对应的原文是 password。毫秒级查询,零计算开销。
第三罪:已发现碰撞漏洞。2004 年,密码学家王小云证明了 MD5 碰撞的可能性——两个不同的输入可以产生相同的 MD5 输出。2008 年,研究人员利用此漏洞伪造了 CA 证书,直接影响全球 HTTPS 安全体系。
无罪声明:MD5 仍然合法的使用场景
- 文件完整性校验:下载大文件后对比官方 MD5 值,确认文件未被篡改。
- 缓存 Key 生成:把 URL 或请求参数哈希成 MD5 作为 Redis 缓存的 Key。需要速度,不需要抗碰撞,MD5 完美胜任。
- 非安全场景去重:对大量文件做 MD5 去重,速度优势明显。
记住这条裁决:MD5 是优秀的完整性工具,是灾难性的密码存储方案。
正确的替代方案
| 算法 | 每秒计算(GPU) | 安全等级 | 推荐场景 |
|---|---|---|---|
| MD5 | 200 亿次 | ❌ 不安全 | 文件校验、缓存 Key |
| SHA-256 | 80 亿次 | ⚠️ 不适合密码 | 数字签名、文件摘要 |
| Bcrypt (cost=12) | 约 40 次 | ✅ 安全 | 用户密码存储 |
| Argon2id | 约 5 次 | ✅✅ 最安全 | 高安全要求场景 |
Bcrypt 每秒只计算 40 次,意味着攻击者破解 1 亿个常见密码需要约 80 年,而不是 MD5 的 0.5 秒。
// Node.js 正确的密码存储示例
const bcrypt = require('bcrypt');
const saltRounds = 12;
// 存储时:生成 Bcrypt 哈希
const hash = await bcrypt.hash(userPassword, saltRounds);
// 验证时:安全对比
const isMatch = await bcrypt.compare(inputPassword, storedHash);
最后的判决
如果今天你打开一个老项目,发现密码存储是 MD5,请把它列为最高优先级的技术债务。数据泄露不是"如果"的问题,是"何时"的问题。唯一的问题是:当那一天到来时,你的用户数据是否让攻击者即使拿到数据库也无从下手。
你可以用 daima.life 的 Bcrypt 哈希生成工具,直接在线生成和验证 Bcrypt 哈希,确认你的系统密码防护方案是否正确。