MD5 和 SHA256 区别:校验和不一致的原因与各算法的适用范围
在浏览器里算了一个字符串的哈希,同事在终端里算同一个字符串,结果两个值对不上。这几乎从来不是哈希函数的问题,而是两边喂给它的 字节不一样:哈希函数看到的只有字节。
看起来一样的文字,字节可能不一样
哈希的输入是字节,所以”文字变成字节”的每一步都算输入的一部分。校验值对不上,常见原因大致按出现频率排列如下:
- 末尾的换行。
echo abc | sha256sum算的是abc加一个换行符,共 4 字节而不是 3 字节。想精确哈希你输入的内容,用printf abc | sha256sum或echo -n abc。 - 换行符风格。 在 Windows 上以 CRLF 检出的文件,和 Linux 上以 LF 保存的同一份文件,对哈希来说就是两个不同的文件。 每行多一个字节,整个摘要就彻底不同。
- 字节顺序标记(BOM)。 有的编辑器和 PowerShell 重定向会在 UTF-8 文件开头写入
EF BB BF。编辑器里看不见,但它是哈希输入的一部分。 - 字符编码。
é在 UTF-8 里是C3 A9,在 Latin-1 里是E9;汉字在 UTF-8 里占三个字节,在 GBK 里占两个字节。如果某个工具 按 UTF-16 算哈希(部分 Windows 接口的默认行为),又会得到第三个结果。下面的哈希工具对文本始终使用 UTF-8。 - 看不见的空白。 从网页或终端复制时带上的一个末尾空格就足够了。
校验值不一致时,先比较长度,再比较别的。工具的状态栏会报告摘要是根据多少字节算出来的;你预期是 3 字节,它却显示 4,那多出来的一个 基本就是换行。
摘要长度和可以拿来自检的值
拿这些值去验证任何一个实现是否正确:
| 算法 | 摘要长度 | 十六进制长度 | 空字符串的摘要 |
|---|---|---|---|
| MD5 | 128 位 | 32 | d41d8cd98f00b204e9800998ecf8427e |
| SHA-1 | 160 位 | 40 | da39a3ee5e6b4b0d3255bfef95601890afd80709 |
| SHA-256 | 256 位 | 64 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 |
对三个字节 abc,MD5 是 900150983cd24fb0d6963f7d28e17f72,SHA-1 是 a9993e364706816aba3e25717850c26c9cd0d89d,
SHA-256 是 ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad。SHA-384 与 SHA-512 的十六进制长度分别是 96 和 128。
输入变动一个比特,大约一半的输出比特会翻转,所以”几乎一样”的摘要和完全不同的摘要同样是错的。
MD5 和 SHA-1 到底哪里坏了
“MD5 不安全”的含义很具体:可以构造出两个内容不同、MD5 却相同的输入。2004 年首次公开演示,此后选择前缀碰撞被用于伪造证书,也出现在真实的恶意 软件里。SHA-1 在 2017 年出现了公开的碰撞实例。对于随机的传输错误、磁盘损坏,两者依然能可靠地发现,这也是你在老软件的下载页旁边还能看到它们的原因。
它们不再能做到的,是抵御攻击者。只要对方有能力构造文件,匹配的 MD5 或 SHA-1 就什么也证明不了。涉及攻击者时,请用 SHA-256 或更强的算法,并记住另一个 弱点:和下载链接放在同一个页面上的校验值,可信度只等于这个页面本身。能替换文件的人同样能替换校验值。真正增加信任的是签名,或者通过另一条可信渠道取得的 校验值。工具在表格旁边标注”MD5 是历史遗留的校验和”,就是这个原因。
到底该用哪个
| 用途 | 建议 |
|---|---|
| 检查文件有没有传输损坏,没有攻击者 | MD5、SHA-1、SHA-256 都行,实际中常选 SHA-256,省得再争论 |
| 对抗恶意篡改的完整性校验 | SHA-256 或更强,并配合签名或独立渠道发布 |
| 给内容生成缓存键、去重指纹 | SHA-256;MD5 只在不怕有人故意制造碰撞时才可以 |
| 存储密码 | Argon2id、scrypt、bcrypt、PBKDF2,不要用上面任何一个 |
| 验证带密钥的消息 | HMAC-SHA-256 |
MD5 出自 1992 年的 RFC 1321,SHA-256 属于 2001 年起标准化的 SHA-2 家族,SHA-384 和 SHA-512 也在其中。此外还有 SHA-3,它的内部构造 完全不同,用于将来需要替换 SHA-2 的场合;下面的工具不包含 SHA-3,只提供 MD5、SHA-1、SHA-256、SHA-384 和 SHA-512 五种。
很多人搜”MD5 和 SHA256 区别”,实际想问的是”我该换哪个”。如果现有系统只是用 MD5 做文件去重或缓存键,且没有攻击者能提交文件,那不换也不会 出问题;如果输入来自不可信的人,就应该换,换的成本通常只是把一个函数名和存储字段的长度改一下(32 个字符变 64 个)。
它们都不能用来存密码
密码哈希追求的是慢。MD5 和 SHA-256 设计目标就是快,拿到泄露数据的攻击者用一块 GPU 每秒能试数十亿个候选,不加盐的快速哈希还会被预计算的彩虹表直接 查出来。请使用专门为此设计的方案,如 Argon2id、scrypt、bcrypt 或 PBKDF2,每个用户单独加盐,并通过维护良好的库调用。先用 SHA-256 对密码预处理一次,并不能 解决问题。
带密钥的哈希与长度扩展攻击
要用密钥验证一条消息,请使用 HMAC。看起来顺手的 SHA-256(secret + message) 容易遭受长度扩展攻击:攻击者只要知道摘要和消息长度,就能在不知道密钥的
情况下,算出”原消息加上额外数据”的摘要。这影响 MD5、SHA-1、SHA-256 和 SHA-512,而 SHA-384 这类截断变体不受影响。HMAC 对它们都消除了这个问题。
下面的工具只计算普通摘要,不提供 HMAC。
十六进制和 Base64 是同一个摘要
十六进制与 Base64 只是同一串字节的两种写法。一个 SHA-256 摘要是 64 个十六进制字符,或者 44 个以 = 结尾的 Base64 字符。浏览器的子资源完整性(SRI)
属性使用 Base64 形式,写成 sha384-...,而多数发布的校验值是十六进制,所以工具把两种写法并排显示。比较十六进制时不要区分大小写:A9993E36 和
a9993e36 是同一个值。
怎么校验文件
sha256sum file.iso # Linux
shasum -a 256 file.iso # macOS
md5 file.iso # macOS,MD5
Get-FileHash file.iso -Algorithm SHA256
certutil -hashfile file.iso SHA256
浏览器里 crypto.subtle.digest('SHA-256', bytes) 支持 SHA-1 和 SHA-2 系列,但不支持 MD5,而且只在安全来源(https 或 localhost)下可用。
本页面的工具用页面内的代码计算 MD5,其余通过这个接口完成,所以在普通 http 下你会得到 MD5,并看到其余算法被禁用的提示。文件内容只在当前标签页读入内存,不会上传;
超过 25 MB 的文件会先请你确认再读取。如果文件的哈希和发布方给出的对不上,请对原始下载文件重新校验,而不是对经过邮件客户端或文本编辑器的副本校验。