MD5 已经不安全了,但它没有死——下载系统镜像、核对数据库脚本、给前端资源算文件指纹,最后都会落到一串 32 位的校验值上。md5在线计算至今仍是最高频的摘要需求之一,关键是要知道它能干到哪一步,又在哪里必须换成 SHA-256。
先记住一句:MD5 不可反解,但它可以被碰撞,也可以被穷举。 这是三件完全不同的事,网上绝大多数讨论把它们混为一谈。
一、MD5 的三个硬事实
| 事实 | 说明 |
|---|---|
| 长度固定 32 位十六进制 | 摘要固定 128 bit = 16 字节,用十六进制写出来就是 32 个字符。一个 1 GB 的镜像和一个字,算出来一样长 |
| 不可逆 | 它是摘要(digest),不是加密。从摘要推不出原文,因为压缩过程丢弃了信息 |
| 雪崩效应 | 改一个 bit,结果面目全非:md5("abc") 与 md5("abd") 完全没有肉眼可见的关联 |
这里要纠正一个普遍误称:「MD5 加密」是错的,正确叫法是 MD5 摘要 / 散列 / 哈希。 加密对应解密,MD5 没有解密这一步。你在网上看到的「MD5 解密」网站,做的事情是查字典——它们预先算好了海量常见明文的 MD5,你提交的哈希被拿去反查表,命中就显示原文,没命中就开始收费推荐所谓的「高级破解」。这跟 MD5 本身安全不安全一点关系都没有,那些站只是提前算好了大量弱明文的摘要在卖。
同理,「MD5 加盐之后能不能解开」这个问题本身就不成立:加盐只改变了摘要的输入,不会让摘要变得可逆。
二、MD5 到底哪里不安全了
不安全的地方精确到一点:抗碰撞性(collision resistance)已被彻底攻破。
- 1996 年:MD5 的设计弱点被提出;2004 年,王小云团队公布了实用的碰撞构造方法,几分钟内就能造出两个内容不同但 MD5 相同的文件
- 2008 年:研究者利用 MD5 碰撞伪造出一个可被浏览器信任的 CA 证书,从此 MD5 在证书领域被全面停用
- 2012 年:Flame 恶意软件利用 MD5 的选择前缀碰撞伪造微软数字签名,实现内网传播
SHA-1 也没好到哪去:
- 2017 年:Google 与 CWI 发布 SHAttered,用两个视觉不同、内容不同的 PDF 文件造出了同一个 SHA-1。官方公布的代价是 2^63.1 量级的压缩函数调用,折合数千 CPU 年加百余 GPU 年
- 2019 年:进一步出现了可选择前缀的 SHA-1 碰撞(SHAmbles)
结论:任何需要「绝不可能出现两个文件哈希相同」的场景(证书、签名、去重、Git 对象 ID),都不要用 MD5 或 SHA-1。 Git 已经支持 SHA-256 形式的仓库,CA 行业早在十多年前就换了算法。
但请务必看清另一面:MD5 的抗原像性(preimage resistance)至今没有被实用攻破。 也就是说,拿到一个 MD5 值去反推原文,目前没有可行手段——这不是因为还没人想到更好的办法,而是压缩过程中本来就丢掉了信息。所以 MD5 不安全,绝不等于它能被「解密」。
三、MD5 唯一还能放心用的地方:完整性校验
下载大文件时,官方发布页往往会同时给出一串校验值,让你确认:
- 下载过程中有没有传输损坏(断点续传、磁盘坏块、代理截断)
- 文件有没有被第三方替换(镜像站植入、网络劫持)
要看清楚的是第二点只防君子:因为 MD5 碰撞可以人为制造,蓄意的攻击者完全可以做出一个 MD5 相同但内容恶意的文件。 MD5 挡得住偶然损坏,挡不住专业对手。
所以实践建议是:
| 场景 | 推荐做法 |
|---|---|
| 自己核对下载文件(防传输损坏) | MD5 够用,毕竟发布页只给了 MD5 |
| 由你自己对外发布文件 | 给 SHA-256,不要只给 MD5 |
| 数字签名、证书、代码签名 | SHA-256 及以上,MD5 / SHA-1 一律不行 |
| 去重、缓存 key、Git 对象ID | 不要用 MD5 当唯一性判据 |
| 存储用户密码 | 一律用 bcrypt / Argon2 / PBKDF2,详见第五节 |
一句话:有得选就用 SHA-256,没得选(别人只给了 MD5)那也照算不误——只是别把安全性压在它身上。
四、字符串哈希最容易翻车的三件事
中文必须明确编码
'你好' 在 UTF-8 下是 6 个字节,在 GBK 下是 4 个字节,在 UTF-16 下又是另一回事。摘要算的是字节而不是字符,所以双方编码不一致时,同一个中文字符串的 MD5 必然对不上,而且不会有任何提示,只有冷冰冰的「校验失败」:
你好(UTF-8) → 7eca689f0d3389d9dea66ae112e5cfd7
你好(GBK) → b94ae3c6d892b29cf48d9bea819b27b9
同一个词,两个完全不同的结果,都是对的。所以跨系统对接时必须先把编码写死——现在通行的做法是统一 UTF-8,别让实现自己去猜。本站的输入区专门把编码这一层做成了可选项(UTF-8 / 十六进制 / Base64),就是为了让「你在算什么」这件事显式化。
末尾的换行和空格
printf 'abc' | md5sum # 900150983cd24fb0d6963f7d28e17f72 ← md5("abc")
echo 'abc' | md5sum # 0bee89b07a248e27c83fc3d5951213c1 ← md5("abc\n"),多了一个换行
两行命令看起来在做同一件事,结果完全不同:echo 默认会在结尾补一个换行,printf 不会。命令行核对哈希时这是最经典的假失败来源——步骤没错,就是对不上。统一用 printf 或 echo -n。
Web 表单里对应的坑是多复制了一个空格、或者 Windows 的 CRLF 换行 \r\n。
BOM
带 UTF-8 BOM 的文件开头有三个不可见字节 EF BB BF,肉眼看不见,哈希一定不同。
五、存密码为什么不能用 MD5 / SHA-256 裸哈希
假设数据库里存的是 md5(password),问题不在 MD5 被碰撞了,而在两点:
- 它太快。 MD5 和 SHA-256 这类通用摘要算法为速度优化。现代 GPU 集群每秒可以做几百亿次量级的尝试,8 位以内的口令基本实时可得
- 没有随机盐。 同一密码得到同一摘要。一次拖库后,攻击者可以直接批量匹配——彩虹表就是这么回事
正确做法是慢哈希 + 随机盐:
// Node 示意:bcrypt 自带随机盐,cost 每 +1,算力成本翻倍
import bcrypt from 'bcryptjs'
const stored = await bcrypt.hash(password, 12) // 写入数据库
await bcrypt.compare(input, stored) // 校验,绝不反解
| 算法 | 定位 |
|---|---|
| bcrypt | 老牌稳妥,多数语言有实现,但对超过 72 字节的输入会截断 |
| PBKDF2 | NIST 认可、兼容性最好(合规审计里最常见) |
| scrypt | 内存硬:同时吃 CPU 和内存,抗 ASIC 与 GPU |
| Argon2 / Argon2id | 2015 年口令哈希竞赛冠军,新系统首推 |
共同点是:它们都很慢,且可以调慢。这才是关键,慢不是缺点,慢是目的。
六、算法选型对照表
| 算法 | 输出长度(十六进制字符) | 典型用途 | 当前安全状态 |
|---|---|---|---|
| MD5 | 32 | 下载文件校验值、老系统兼容 | 抗碰撞已破,禁止用于安全场景 |
| SHA-1 | 40 | 老版 Git 对象 ID、遗留接口签名 | 抗碰撞已破(SHAttered),应淘汰 |
| SHA-256 | 64 | 发布文件校验、证书、签名、区块链 | 安全,通用首选 |
| SHA-384 | 96 | 需要更长摘要的场景 | 安全 |
| SHA-512 | 128 | 64 位平台上性能有时优于 SHA-256 | 安全 |
| bcrypt / Argon2 / PBKDF2 | 随算法而定 | 唯一应该用来存密码的一类 | 安全(按推荐参数使用) |
识别摘要类型的土办法:数长度。32 位是 MD5、40 位是 SHA-1、64 位是 SHA-256、96 位是 SHA-384、128 位是 SHA-512。本站的「校验值比对」就是按这个规则自动识别的,且比对时自动忽略大小写和空格。
七、命令行对照
# Linux
md5sum install.iso
sha256sum install.iso
# macOS(无 md5sum / sha256sum)
md5 install.iso
shasum -a 256 install.iso
md5 -s "要计算的字符串" # 字符串,注意 shell 会先处理引号
printf '%s' "要计算的字符串" | md5 # 推荐:不吃尾随换行
# Windows CMD
certutil -hashfile install.iso MD5
certutil -hashfile install.iso SHA256
# Windows PowerShell
Get-FileHash install.iso -Algorithm SHA256 | Format-List
注意 macOS 的 md5 -s 会把传入的字符串当作不含换行的内容,而 Linux 下 echo 管道会带换行——跨平台对结果时优先用文件而不是字符串。
八、md5在线计算怎么用:核对一个下载文件
完整流程就三步,全部在本机完成:
- 把文件拖进文件摘要区(支持多选,单个超过 256 MB 会被拒绝,因为浏览器要把内容读进内存)
- 勾选要算的算法,默认已选 MD5 / SHA-1 / SHA-256
- 把官网给的校验值粘贴进「校验值比对」,工具自动忽略大小写与空格后比对,直接告诉你一致还是不一致
实现上有两个细节值得一提:WebCrypto 规范里根本没有 MD5,只有 SHA-1 和 SHA-2 系列,所以本机工具的 MD5 是自己按 RFC 1321 实现的;SHA-256/384/512 则交给浏览器原生的 crypto.subtle,有硬件加速。文件全程不上传。
九、高频问题速查
| 问题 | 答案 |
|---|---|
| MD5 能解密吗 | 不能。网上的「解密」是查表,只对弱明文有效 |
| 结果是大写还是小写 | 摘要本身无大小写含义,校验时忽略大小写即可 |
| 所谓 16 位 MD5 是什么 | 把 32 位结果截断取出的一段,不是另一种算法,跨系统对不上很正常 |
| MD5 相同,文件内容就一定相同吗 | 不一定。这就是「碰撞」:内容不同的两个文件可以算出同一个摘要 |
| 1 GB 文件的 MD5 有多长 | 32 位,和空字符串一样长 |
| 换 SHA-512 更「安全」吗 | 校验场景 SHA-256 已完全够用;位数长不等于更抗碰撞 |
十、对着做的清单
- 对外发布下载文件时,校验值至少给 SHA-256,MD5 可以作为兼容保留
- 下载别人的文件时,优先核对官方给的 SHA-256;只有 MD5 也照样核对,它能抓住绝大多数传输损坏
- 涉及签名、证书、代码签名的场景,直接排除 MD5 和 SHA-1
- 项目里还在用
md5(password)的,本周就换成 bcrypt / Argon2,SHA-256(password)同样属于必须改的行列——它是快哈希,只是口味不同 - 跨系统对摘要之前,先对齐编码(统一 UTF-8)、去掉 BOM、确认没有多余换行与空格
- 别用一个哈希当唯一性判据,需要去重就加文件大小和字节数一起判
相关工具:哈希摘要计算(MD5 / SHA 全系列)|Base64 / 编码转换|JSON 格式化
全部纯前端实现,输入的内容不上传服务器。