土薯工具 Toolshu.com 登录 用户注册

MD5 是哈希不是加密:原理、破解方式与密码存储的正确姿势

原创 作者:bhnw 于 2026-04-06 22:51 发布 3次浏览 收藏 (0)

MD5 到底是什么

很多人把 MD5 叫做"MD5加密",这个说法从一开始就是错的。

MD5 是一种哈希函数(Hash Function),不是加密算法。两者有本质区别:

  • 加密:可逆操作,有密钥,能解密还原原始数据
  • 哈希:不可逆操作,无密钥,理论上无法从结果推回原始数据

MD5 的作用是把任意长度的输入,变成一个固定 128 位(32个十六进制字符)的输出,称为"摘要"或"指纹"。

MD5("hello")    = 5d41402abc4b2a76b9719d911017c592
MD5("hello!")   = 9b56e4f280d7b27a6c8d0c11d5a8cb7d
MD5("一篇很长的文章...") = d41d8cd98f00b204e9800998ecf8427e

三个特性:

  1. 单向性:无法从哈希值逆推原始内容
  2. 固定长度:无论输入多长,输出始终 32 位
  3. 雪崩效应:输入改动一个字节,输出完全不同

MD5 能被"破解"吗

严格来说,MD5 无法被"解密"——因为它不是加密。但它可以被暴力破解查表攻击

暴力破解

最简单的思路:把常见密码逐个计算 MD5,和目标哈希值对比。

计算 MD5("123456")    = e10adc3949ba59abbe56e057f20f883e  ← 匹配!

现代 GPU 每秒可以计算数十亿次 MD5,123456passwordqwerty 这类常见密码在不到一秒内就能找到。

彩虹表攻击

暴力破解需要实时计算,而彩虹表是预先计算好的海量明文-哈希值对照表,空间换时间。

攻击者拿到一个 MD5 哈希值,直接在表里查找,瞬间得到原始密码。网上有公开的 MD5 彩虹表,覆盖数百亿条常见密码和短字符串组合。

实际上,很多"MD5在线解密"网站的原理就是这个——它们维护了一张巨大的哈希-明文数据库,输入哈希值查表返回结果,不是真正的"解密"。

MD5 碰撞

2004年,研究人员找到了 MD5 的碰撞攻击方法:可以构造出两个不同的输入,产生完全相同的 MD5 值。这从根本上动摇了 MD5 作为完整性校验工具的可信度。

文件A 和 文件B 内容完全不同,但 MD5 值完全相同

这意味着攻击者可以伪造一个与合法文件 MD5 值相同的恶意文件,绕过完整性校验。


为什么还有人用 MD5 存密码

历史原因。MD5 在 1991 年发布时被认为足够安全,大量早期系统将其用于密码存储。随着计算能力的提升和攻击方法的演进,MD5 的安全性早已不够,但遗留系统的迁移成本很高,导致至今仍有系统在用。

2012年 LinkedIn 数据泄露事件中,约 650 万条密码以未加盐的 SHA-1 哈希存储;更早的数据泄露中,大量网站使用的正是裸 MD5。泄露后,攻击者在数小时内就破解了绝大多数密码。


正确的密码存储方案

加盐(Salt)

给每个密码在哈希前拼接一段随机字符串(盐值),使得相同的密码产生不同的哈希结果,从而让彩虹表完全失效。

salt = "xK92mP"  // 每个用户随机生成,存入数据库
hash = MD5(password + salt)

但即便加盐,MD5 本身计算太快,GPU 暴力破解仍然可行。

bcrypt:专为密码存储设计

bcrypt 是目前最主流的密码哈希方案,有几个关键特性:

1. 内置盐值:每次哈希自动生成随机盐,无需手动处理。

2. 计算成本可调:通过 cost factor 参数控制哈希运算的计算量,随硬件提升可以相应调高,保持破解难度。

3. 故意很慢:正常用户登录只需验证一次,慢一点无所谓;但攻击者需要暴力尝试数十亿次,慢就是致命的。

import bcrypt

# 注册:生成哈希
password = b"user_password"
hashed = bcrypt.hashpw(password, bcrypt.gensalt(rounds=12))
# 存入数据库的是 hashed,不是原始密码

# 登录:验证
bcrypt.checkpw(password, hashed)  # 返回 True/False

Argon2:更现代的选择

Argon2 是 2015 年密码哈希竞赛的冠军算法,在 bcrypt 基础上增加了内存占用控制,进一步提升了对 GPU 和 ASIC 暴力破解的抵抗力。新项目首选 Argon2id。

from argon2 import PasswordHasher

ph = PasswordHasher(time_cost=2, memory_cost=65536, parallelism=2)
hashed = ph.hash("user_password")
ph.verify(hashed, "user_password")  # 验证

scrypt

与 Argon2 类似,也是内存密集型哈希,Node.js 标准库内置支持,适合 JavaScript 后端。


MD5 现在还能用在哪里

MD5 不适合安全场景,但在以下非安全场景中仍然合理:

文件完整性校验(非安全环境)

下载文件后对比 MD5 值,确认文件未被损坏(注意:不能防止有意篡改,只能检测传输错误)。

# Linux/macOS
md5sum filename.zip

# macOS
md5 filename.zip

生成唯一标识符

对内容取 MD5 作为缓存 key、去重 ID 等,不涉及安全的场景。

数据库内容去重

对大文本字段取 MD5 建索引,快速判断是否重复。

非安全场景的数据指纹

日志系统、数据管道里对数据做快速摘要,用于追踪和对比。


一句话总结

场景 推荐方案
用户密码存储 bcrypt 或 Argon2id
文件完整性校验(安全) SHA-256
文件完整性校验(非安全) MD5 可用
数字签名 RSA + SHA-256
缓存 Key / 去重 ID MD5 可用

密码存储永远不要用 MD5,不管加不加盐。


在线工具

如果你需要计算一段文本的 MD5 值用于数据校验或测试,可以使用 toolshu.com 的 MD5 在线加密工具,支持 16 位和 32 位输出,大小写均可,在浏览器本地运算不上传数据。

发现周边 发现周边
评论区

加载中...