MD5(消息摘要算法 5)曾是文件完整性校验和密码存储的首选哈希函数。如今,它成了一个警示故事。虽然 MD5 在重复检测等非安全任务中仍然快速有用,但其密码学弱点使其在安全敏感应用中变得危险。本文深入探讨 MD5 的陷阱、为何不再推荐用于密码哈希,以及如何在必须使用时正确操作。

MD5 如何工作
MD5 产生 128 位(16 字节)的哈希值,通常表示为 32 字符的十六进制字符串。该算法以 512 位块处理输入,应用一系列逻辑运算、位旋转和非线性函数。输出是确定性的:相同输入总是产生相同哈希。
为什么 MD5 在安全上已被攻破
碰撞攻击
碰撞是指两个不同输入产生相同哈希。2004 年,研究人员展示了实用的 MD5 碰撞。到 2008 年,攻击者可以生成碰撞的 X.509 证书。如今,在消费级硬件上几秒钟即可计算出一个碰撞。
原像和第二原像抗性
MD5 的原像抗性(给定哈希,找到产生它的输入)也被削弱。虽然不如碰撞抗性严重,但已不再被认为能抵御坚定攻击者。
速度是敌人
MD5 设计为快速。对于密码哈希,速度是负担:攻击者使用 GPU 每秒可尝试数十亿次猜测。现代 GPU 每秒可计算超过 100 亿个 MD5 哈希,使暴力破解变得轻而易举。
常见陷阱
- 使用 MD5 存储密码:永远不要用纯 MD5 存储密码。使用专门的密码哈希算法如 bcrypt、scrypt 或 Argon2。
- 依赖 MD5 进行完整性校验:攻击者可以同时替换文件和其 MD5 哈希。改用 HMAC 或数字签名。
- 认为“双重 MD5”或“加盐”能修复它:加盐并多次哈希(例如 MD5(MD5(password)))并不能修复根本的碰撞弱点,只能边际地减缓暴力破解。
- 假设 MD5 对去重是唯一的:虽然在小数据集中碰撞很少见,但可能发生。对于大规模系统,使用 SHA-256 或 SHA-3。
- 混淆哈希与加密:哈希是单向的;加密是可逆的。切勿将 MD5 用作密码。
MD5 何时仍可接受
MD5 并非完全无用。在非安全任务中,当速度至关重要且碰撞风险可忽略时,它可以接受:
- 文件去重:检查两个文件是否相同(例如备份系统)。
- 数据完整性校验:检测意外损坏(而非恶意篡改)。
- 缓存键:为缓存查找生成简短快速的键。
- 非安全标识符:为已知输入集创建紧凑标识符。
工作示例:密码哈希的错误做法
让我们模拟一个典型的(糟糕的)密码存储场景。使用 Python 演示 MD5 为何失败。
import hashlib
# 模拟用户数据库(不安全 - 请勿使用)
database = {}
def register_unsafe(username, password):
# 直接 MD5 哈希 - 无盐
hashed = hashlib.md5(password.encode()).hexdigest()
database[username] = hashed
def login_unsafe(username, password):
hashed = hashlib.md5(password.encode()).hexdigest()
return database.get(username) == hashed
# 注册用户
register_unsafe("alice", "password123")
# 攻击者的彩虹表查找
rainbow_table = {
"482c811da5d5b4bc6d497ffa98491e38": "password123",
"5f4dcc3b5aa765d61d8327deb882cf99": "password",
# ... 数百万条
}
# 攻击者找到数据库中的哈希
stored_hash = database["alice"]
if stored_hash in rainbow_table:
print(f"密码破解: {rainbow_table[stored_hash]}")
输出:
密码破解: password123
简单的彩虹表立即揭示密码。即使加盐,MD5 的速度也允许攻击者快速暴力破解弱密码。
对比:MD5 与现代替代方案
| 特性 | MD5 | SHA-256 | bcrypt | Argon2 |
|---|---|---|---|---|
| 位长度 | 128 | 256 | 可变 | 可变 |
| 碰撞抗性 | 已攻破 | 安全 | 不适用(内置盐) | 不适用(内置盐) |
| 速度 | 非常快 | 快 | 慢(可配置) | 慢(可配置) |
| 密码哈希 | 否 | 否 | 是 | 是 |
| 使用场景 | 非安全 | 完整性、签名 | 密码存储 | 密码存储 |
常见问题
MD5 完全被攻破了吗?
对于碰撞抗性,是的。对于原像抗性,它被削弱但未完全攻破。然而,存在实用碰撞意味着 MD5 不应在任何安全上下文中使用。
我可以对密码使用 MD5 加盐吗?
不能。加盐防止彩虹表攻击,但 MD5 的速度仍然允许暴力破解。使用缓慢的自适应算法如 bcrypt。
文件完整性应该用什么替代 MD5?
对于意外损坏,MD5 没问题。对于安全(例如验证下载),使用 SHA-256 或 SHA-3 配合可信签名。
为什么有些系统仍在使用 MD5?
遗留兼容性、性能需求或非安全用例。始终评估威胁模型。
如何在代码中生成 MD5 哈希?
大多数语言都有内置支持。例如在 Python 中:hashlib.md5(b"hello").hexdigest() 返回 "5d41402abc4b2a76b9719d911017c592"。尝试在我们的哈希文本工具中测试。
结论
MD5 是更简单时代的遗物。虽然它仍有小众用途,但在安全上已被根本攻破。对于密码存储,始终使用专门的密码哈希算法。对于其他安全敏感任务,优先选择 SHA-256 或 SHA-3。当确实使用 MD5 时,了解其局限性,切勿依赖它来抵御恶意行为者。