我们一度用 MD5 做安装包校验。有天同事说"哈希对不上,这个包有问题",于是整个团队把 CDN 翻了一遍,最后原因是他下载的其实是 CDN 返回的 404 页面——一个只有几百字节的 HTML,哈希当然完全不同。另一次更麻烦:老系统把用户密码直接存 MD5,被拖库后不到两天就有人用现成的碰撞表还原出弱口令。这两件事让我把"哈希用来做什么"和"该用哪种算法"分得很清楚:校验传输有没有坏,和防止别人伪造内容,是完全不同的两个要求。
一、哈希的三个性质,决定了它只能做这三类事
- 定长:不管输入 3 个字节还是 3 个 GB,SHA-256 输出永远是 256 位,也就是 64 个十六进制字符。
- 单向:从摘要推不回原文。这和加密有本质区别——加密是可逆的,有密钥能还原,目的是保密;哈希的目的是"指纹"。
- 雪崩效应:原文改 1 个字符,摘要会有大约一半的位发生变化。空字符串的 MD5 是
d41d8cd98f00b204e9800998ecf8427e、SHA-256 是e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855,而末尾只多加一个空格,出来的就是两串毫不相干的值。这两个固定值还能当自检用:算出来对不上,说明工具或输入环节有问题。
因此它能可靠回答三个问题:这两份内容是否一致;这份内容是否被改动过;以及在不暴露原文的前提下能否证明我知道原文。它不能回答的问题是"这段密文是什么"。
二、MD5 不安全到什么程度,还剩下什么用
| 算法 | 输出 | 十六进制长度 | 现状 | 适合的用途 |
|---|---|---|---|---|
| MD5 | 128 位 | 32 字符 | 碰撞可在普通硬件上构造,签名证书领域已宣告失效 | 非对抗场景的完整性校验、数据去重分桶 |
| SHA-1 | 160 位 | 40 字符 | 同样存在实际碰撞,主流浏览器与证书体系已淘汰 | 兼容旧系统的校验值 |
| SHA-256 | 256 位 | 64 字符 | 当前推荐,广泛用于签名、区块摘要、制品校验 | 安全场景默认选择 |
| SHA-512 | 512 位 | 128 字符 | 强度更高,摘要更长、传输与人工核对成本上升 | 高强度要求、需要更长摘要时 |
"碰撞"指有人刻意构造出两个内容不同、摘要却相同的输入。一旦可被制造,"摘要相同即内容可信"这个前提就被破坏——这才是 MD5 被赶出安全场景的原因。但反过来看,下载中断、CDN 回源错误、文件被脚本误改,这些都不是有人在构造碰撞,MD5 依然够用。真正的分歧点只有一个:有没有对手会主动构造第二份内容。
三、口令存储是第三件事,别混进来
把 MD5 或 SHA-256 拿来存密码,是同一系列里最贵的误用。快的哈希恰恰是攻击者的朋友:一块消费级显卡每秒能试算海量候选,撞库脚本配上现成的彩虹表,常见弱口令几乎瞬间还原。正确做法是用带盐的慢哈希——bcrypt、scrypt、Argon2id 这类自带盐值并可调工作因子的算法,让每次验证都要付出可控的计算成本。盐的作用是相同密码得到不同摘要,从而废掉批量预计算;它不是密钥,可以明文存储。
四、我的实际流程
- 拿到校验值先确认算法名与格式。很多"对不上"来自 16 位与 32 位混用:所谓 16 位 MD5 只是取 32 位结果的第 9 到第 24 个字符,天生就"对不上"完整值;同理还有大小写显示差异,
a–f与A–F表达的是同一个摘要。 - 文本类要在同一个工具里一次算完再比。我的习惯是把待校验内容贴进哈希计算器,MD5、SHA-1、SHA-224、SHA-256、SHA-384、SHA-512 六种摘要一次列出来,逐行和对方公布的值对照,需要哪一行就单独复制哪一行。很多历史系统只给 MD5,而新规范要求 SHA-256,一次全出就不用反复换工具重算第二遍;比对时顺手把大小写统一,能省掉一半"看着不一样"的误判。
- 文件走文件校验。安装包这类几百 MB 的东西用文件 MD5 计算,可以一次拖进多个文件同时得到 MD5、SHA-1、SHA-256,比对是否下错包时很直观。
- 接口签名用 HMAC,不用裸哈希拼密钥。
md5(secret + params)这种写法有过被绕过的记录(涉及长度扩展等构造方式),改用 HMAC-SHA256 就把密钥的参与方式交给一个专门设计过的结构。在同一个计算器里填密钥即可得到 HMAC 值,用来核对客户端签名。
五、常见疑问
Q:哈希和加密到底有什么区别?
哈希单向、定长、不需要密钥,用于校验与指纹;加密可逆、需要密钥,用于保密传输与存储。要还原内容就只能用加密,不能指望"解哈希"。
Q:SHA-256 能反推出原文吗?
不能。只能靠穷举候选再比对摘要,所以原文本身的随机度决定安全性——口令太短,换 SHA-256 也一样被撞出来。
Q:文件校验值对不上,先怀疑什么?
先怀疑"比的不是同一个东西":下载到了错误页或被截断、文本换行符被改(CRLF 与 LF 会让纯文本摘要完全不同)、编码从 UTF-8 变成 GBK、以及 16 位对 32 位。把文件大小和行数先看一眼,能排除八成的误报。
Q:能不能自己发明一种哈希方案?
不建议。安全强度来自公开算法经过多年攻击验证,而不是把几个摘要串起来或自定义排序这类组合,后者往往引入了新的可构造点。选 SHA-256 或 SHA-512,加 HMAC,再用成熟的口令哈希算法,覆盖绝大多数需求。
Q:数据去重、找重复文件也要用 SHA-256 吗?
不涉及对手时用 MD5 更快也更省事,只需要"两份内容是否相同"。真正要防篡改的地方才需要升级到 SHA-256,这时也要一并考虑摘要本身的分发渠道是否可信。
那两次事故给我的结论很朴素:先说清楚这次比对是为了"防传输出错"还是"防人为伪造",算法的选择自己就会定下来;而"对不上"的时候,第一动作应该是确认两边算的是不是同一段字节,而不是先怀疑算法被攻破。