跳到主内容
d.devtul.fun
EN
安全 · 2026-09-05

什么是哈希?MD5、SHA-1、SHA-256 以及该用哪一个

哈希函数就是一台机器:你喂给它任何东西——一个密码、一段 4 GB 的视频、或者一个字节——它吐出来一串长度固定的字符。同样的输入永远得到同样的输出,不同的输入几乎总是得到不同的输出。这个输出叫摘要、指纹,或者直接叫哈希值。它有一个最本质的特点:不可逆。这个"单向"正是哈希的全部意义所在。

哈希函数到底在做什么

你调用哈希函数,不是为了像加密那样"打乱"数据,而是为了把数据浓缩成又小又稳定的东西。不管输入多大,机制都是一样的:

输入:"The quick brown fox"  ->  摘要:37ab…(SHA-256 是 64 个十六进制字符)
输入:一部两小时的电影        ->  摘要:9f2c…(还是 64 个十六进制字符)

摘要的长度不会随着输入变大而变大。这个固定长度,正是哈希便于存储和比较的原因。

你可以信赖的四个性质

  • 确定性。同一个字节序列哈希两次,结果逐字节相同。这正是校验和能被信任的前提。
  • 定长输出。SHA-256 永远输出 256 位,不管你给了它什么。
  • 单向。给了摘要,你无法还原出输入。没有"反哈希"按钮,谁说有,谁就是在卖东西。
  • 雪崩效应。输入只要改一位,输出大约有一半的位会翻转,两段摘要看起来完全不相关。

你可以用两个几乎一样的字符串亲眼看看雪崩效应:

$ echo -n "hello"  | sha256sum
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824

$ echo -n "hellp"  | sha256sum
1dd9f635fc9d209cbfa643af332d5b9f6d4b66cb3c8c1756a65d9d19c8a6833c

一个五个字母的单词,只改了一个字母,整个 64 位的输出就全变了。这就是雪崩效应在工作。

哈希 vs 加密 vs 编码

这三样东西经常被混为一谈,通常是因为它们都能把可读的数据变成一堆像乱码一样的字符。但它们解决的是三个完全不同的问题。

特性哈希加密编码
可逆吗否(单向)有密钥就能还原能,无需密钥
需要密钥吗不需要需要不需要
输出长度固定约等于输入约等于输入
能证明什么完整性 / 身份机密性传输兼容性
典型场景密码、校验和、签名存密钥、TLSJSON 里的 Base64、URL

最快的区分办法:编码只是换了一种格式、不带任何保密性(谁都能解码);加密靠密钥把数据藏起来;哈希是故意把原文丢掉。

算法阵容:谁还站得住

不是每个哈希算法都安全可用,而且这个差距在过去二十年里越拉越大。

算法摘要长度2026 年的状态
MD5128 位碰撞已破解,只能做校验和,不能用于安全
SHA-1160 位碰撞已破解(SHAttered,2017),该退役了
SHA-256256 位稳妥,校验和与签名的默认选择
SHA-512512 位稳妥,在 64 位硬件上比 SHA-256 还快
BLAKE3可配置新锐,极快且可并行,新代码的好选择

MD5 和 SHA-1 是那两个你必须停止在安全场景里使用的算法。它们用来检测"下载文件是不是意外损坏了"还凑合,因为那时没有攻击者在刻意伪造一个碰撞文件。可一旦有人有动机去构造第二个相同摘要的文件,这两个算法就直接倒下。

碰撞与长度扩展攻击

碰撞指的是两个不同的输入产生了相同的摘要。256 位的输出意味着可能的输入远多于可能的摘要,所以碰撞在数学上必然存在——关键在于攻击者能不能主动找到一个。对 MD5 和 SHA-1 来说,他们能,而且在一台笔记本上几秒钟就行。

另一个坑叫长度扩展攻击。SHA-256 和 SHA-1 都建立在 Merkle–Damgård 构造之上,这意味着如果你知道 H(message) 却不知道 message 本身,往往还能往后追加数据、算出 H(message || extra),而根本不需要看到原始消息。这也就是为什么你绝不能自己用 hash(密钥 || 数据) 来当"MAC"用——应该用 HMAC(下一篇会讲)。

加盐与加 pepper:这是两回事

这两个词经常被不懂的人混着用。它们不是一回事。

  • 盐(salt)是每个用户单独生成的随机值,存在哈希旁边。它阻止攻击者用预先算好的彩虹表,也防止两个密码相同的用户拿到相同的哈希。它不保密——就明晃晃躺在数据库里。
  • pepper是整套应用共享的一个 secret 值,存在数据库之外(环境变量、KMS)。它保护的是"数据库泄露了但 pepper 没泄露"这种情况。如果攻击者同时拿到了数据库和 pepper,它就毫无作用。

如果你在存密码,你应该用 bcrypt、scrypt、Argon2id 这类本来就内置加盐的密码哈希算法,而不是自己手搓盐和 pepper。做消息认证用 HMAC 时,那个密钥就扮演了 pepper 的角色。

到底该用哪个

场景用这个为什么
校验下载的文件SHA-256标准、抗碰撞的校验和
存储用户密码Argon2id 或 bcrypt故意慢且加盐
给消息做认证HMAC-SHA256带密钥,免疫长度扩展
只是让二进制塞进文本Base64(那是编码!)根本不是哈希
把数据藏起来不给人看AES-GCM(那是加密!)哈希做不到这个

最常见的错误,就是图 MD5"快",拿它去做需要抵御攻击者的活儿。对校验和来说快是优点,对密码存储来说快是致命伤。选算法要看对手,不要看跑分。

想自己算几个摘要、亲眼看看雪崩效应?哈希生成工具 能把同一段文本用 SHA-256、SHA-512 和 BLAKE3 并排算出来。

继续阅读