MD5 与 SHA-256:你应该使用哪种哈希算法?
哈希是计算机领域最基础的操作之一——但选择了错误的算法,可能让你的系统暴露在碰撞攻击、数据损坏或不必要的性能开销之下。本文将对比四种最常用的哈希算法,并为你提供一个清晰的选型框架。
什么是哈希函数?
密码学哈希函数接受任意输入数据,生成固定长度的输出(称为”摘要”或”哈希值”)。优秀的哈希函数具备三个特性:
- 确定性:相同的输入始终产生相同的输出。
- 单向性:无法从哈希值反推出原始输入。
- 抗碰撞性:在计算上不可能找到两个产生相同哈希值的不同输入。
当第 3 个特性被攻破时,该算法就被认为是”密码学上已被破解的”——MD5 和 SHA-1 正是如此。
算法对比一览
| 属性 | MD5 | SHA-1 | SHA-256 | SHA-512 |
|---|---|---|---|---|
| 输出长度 | 128 位(32 个十六进制字符) | 160 位(40 个十六进制字符) | 256 位(64 个十六进制字符) | 512 位(128 个十六进制字符) |
| 分组大小 | 512 位 | 512 位 | 512 位 | 1024 位 |
| 发布年份 | 1991 | 1995 | 2001 | 2001 |
| 设计者 | Ron Rivest | NSA / NIST | NSA / NIST | NSA / NIST |
| 抗碰撞性 | 已被破解(2004) | 已被破解(2017) | 安全 | 安全 |
| 速度(相对) | 最快 | 快 | 中等 | 中等(在 64 位系统上更快) |
| NIST 状态 | 已弃用 | 已弃用 | 推荐使用 | 推荐使用 |
MD5:速度快但已被破解
MD5(消息摘要算法第 5 版)由 Ronald Rivest 于 1991 年设计,在 1990 年代到 2000 年代初期成为校验和的事实标准。它生成 128 位的哈希值,以 32 个十六进制字符表示。
为什么 MD5 已被破解
2004 年,王小云展示了针对 MD5 的实际碰撞攻击。到 2008 年,研究人员利用 MD5 碰撞创建了伪造的 SSL 证书,证明这种攻击并非纸上谈兵。如今,在消费级硬件上只需数秒即可生成 MD5 碰撞。
// 生成 MD5 哈希(仅用于非安全场景)
// Web Crypto API 不支持 MD5 — 需要使用第三方库
import { md5 } from 'hash-wasm';
const hash = await md5('Hello, World!');
console.log(hash);
// → 'bea8252ff4e80f41719ea13cdf007273' (32 hex chars)
MD5 仍然可用的场景
尽管在密码学上已被破解,MD5 在非安全场景中仍然有用:
- 文件去重:检测存储系统中的重复文件
- 缓存键:为缓存查找生成短小、确定性的键
- 数据完整性校验:快速验证数据是否意外损坏(而非被蓄意篡改)
- 遗留系统兼容:与需要 MD5 的旧系统进行互操作
关键区别在于:MD5 可以防止意外损坏,但无法防止蓄意篡改。
SHA-1:已弃用但仍在使用
SHA-1(安全散列算法 1)由 NSA 设计,NIST 于 1995 年发布。它生成 160 位的哈希值,以 40 个十六进制字符表示。
2017 年,Google 和 CWI Amsterdam 展示了首个实际的 SHA-1 碰撞(“SHAttered” 攻击),生成了两个不同的 PDF 文件却具有相同的 SHA-1 哈希值。主流浏览器和证书颁发机构早在 2016 年就已开始拒绝 SHA-1 证书。
仅在需要与要求 SHA-1 的系统兼容时使用(例如,Git 使用 SHA-1 作为提交哈希,但正在向 SHA-256 过渡)。所有新开发项目请选择 SHA-256 或更强的算法。
SHA-256:当前的标准
SHA-256 属于 SHA-2 系列,由 NSA 设计,NIST 于 2001 年发布。它生成 256 位的哈希值,以 64 个十六进制字符表示,是 2026 年几乎所有哈希需求的推荐算法。
// 使用 Web Crypto API 生成 SHA-256 哈希(浏览器原生支持)
async function sha256(text) {
const data = new TextEncoder().encode(text);
const hash = await crypto.subtle.digest('SHA-256', data);
return Array.from(new Uint8Array(hash))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
}
const hash = await sha256('Hello, World!');
console.log(hash);
// → 'dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f'
为什么 SHA-256 是默认之选
- 无已知攻击:截至 2026 年,不存在针对 SHA-256 的实际碰撞或原像攻击
- 浏览器原生支持:所有现代浏览器均通过 Web Crypto API 提供支持——无需额外库
- 行业标准:被 TLS 证书、Bitcoin、包管理器(npm、pip)、Docker 镜像摘要及大多数完整性验证系统采用
- NIST 认证:推荐用于所有安全敏感的应用
SHA-512:需要更高安全性时的选择
SHA-512 生成 512 位的哈希值,以 128 个十六进制字符表示。它使用 1024 位的分组大小(SHA-256 为 512 位),因此在 64 位处理器上比 SHA-256 更快,因为每个周期能处理更多数据。
# Python: SHA-512 和 SHA-256 一样简单
import hashlib
hash_256 = hashlib.sha256(b'Hello, World!').hexdigest()
hash_512 = hashlib.sha512(b'Hello, World!').hexdigest()
print(f"SHA-256: {hash_256}") # 64 chars
print(f"SHA-512: {hash_512}") # 128 chars
适合使用 SHA-512 的场景:
- 需要更大的安全余量(256 位碰撞抗性,SHA-256 为 128 位)
- 平台为 64 位且注重性能(SHA-512 在 x86-64 上可比 SHA-256 快 1.5 倍)
- 协议或规范要求使用(某些证书方案、特定合规要求)
对于大多数应用,SHA-256 已经足够。SHA-512 更长的哈希值会使存储和带宽翻倍,但对典型场景并没有实际的安全优势。
选型框架:该用哪种哈希
用于文件完整性和校验和
使用 SHA-256。 它是验证下载文件、比较文件内容、确保数据未损坏的标准方案。如果你要替换现有的基于 MD5 的系统,SHA-256 是直接替代方案。
# 验证下载文件的完整性
sha256sum downloaded-file.tar.gz
# 将输出与发布的哈希值进行比较
用于密码存储
不要直接使用 MD5 或 SHA-256。 通用哈希函数对于密码哈希来说速度太快——攻击者每秒可以尝试数十亿次猜测。应使用专用的密码哈希算法:
| 算法 | 状态 | 说明 |
|---|---|---|
| Argon2id | 推荐 | 2015 年密码哈希竞赛冠军;内存硬化 |
| bcrypt | 良好 | 广泛支持;内置盐值;可调工作因子 |
| scrypt | 良好 | 内存硬化;被部分加密货币系统使用 |
| PBKDF2 | 可接受 | NIST 认证但非内存硬化;建议 ≥600,000 次迭代 |
| SHA-256 | 不合适 | 速度过快;无内置盐值;易受 GPU 攻击 |
| MD5 | 危险 | 已被破解且速度过快;轻松可破解 |
// 错误做法:不要用 SHA-256 哈希密码
const hash = await sha256(password); // 每秒可破解数十亿次
// 正确做法:使用 bcrypt(Node.js 示例)
import bcrypt from 'bcrypt';
const hash = await bcrypt.hash(password, 12); // 12 轮 ≈ 250ms
const isValid = await bcrypt.compare(input, hash);
用于 HMAC 和消息认证
使用 SHA-256 配合 HMAC。 HMAC(基于哈希的消息认证码)将哈希函数与密钥结合,同时验证完整性和真实性:
// HMAC-SHA256 用于 Webhook 签名验证
async function verifyWebhook(payload, signature, secret) {
const key = await crypto.subtle.importKey(
'raw', new TextEncoder().encode(secret),
{ name: 'HMAC', hash: 'SHA-256' }, false, ['verify']
);
const sig = Uint8Array.from(atob(signature), c => c.charCodeAt(0));
return crypto.subtle.verify('HMAC', key, sig, new TextEncoder().encode(payload));
}
用于内容寻址存储
使用 SHA-256。 Git、Docker 和 IPFS 都使用内容寻址存储,内容的哈希值即为其地址。SHA-256 提供了足够的碰撞抗性,可在数十亿对象中保证唯一性。
性能基准测试
在现代 x86-64 处理器上的相对哈希速度(越高越快):
| 算法 | 吞吐量 (MB/s) | 相对速度 |
|---|---|---|
| MD5 | ~3,200 | 1.0x(基准) |
| SHA-1 | ~2,400 | 0.75x |
| SHA-256 | ~1,500 | 0.47x |
| SHA-512 | ~2,100 | 0.66x |
注意:SHA-512 在 64 位处理器上比 SHA-256 更快,这是因为它的内部状态更宽。在 32 位系统上情况则相反。对于大多数应用来说,速度差异无关紧要——用任何一种算法对 1 MB 文件进行哈希都不到 1 毫秒。
常见误区
1. 将 MD5 用于安全场景
MD5 碰撞生成已经极其简单。绝不要将 MD5 用于数字签名、证书验证或任何攻击者可能构造恶意输入的场景。
2. 使用 SHA-256 哈希密码
SHA-256 不是密码哈希函数。它速度太快,没有内置盐值,容易受到彩虹表和 GPU 加速暴力破解攻击。请使用 Argon2id 或 bcrypt。
3. 认为哈希长度等于安全性
512 位哈希并不自动比 256 位哈希”更安全”。SHA-256 的安全余量(128 位碰撞抗性)已经远超暴力破解的能力范围。应根据实际需求选择,而不是哈希长度。
4. 自行组合哈希
SHA256(MD5(data)) 或 MD5(data + salt) 这样的模式并不能神奇地修复已被破解的算法。请使用单一的、经过充分验证的算法(SHA-256)或正规的构造方式(HMAC)。
亲自试试
使用我们的哈希生成器即时生成和比较 MD5、SHA-1、SHA-256 和 SHA-512 哈希——粘贴文本或拖入文件,即可并排查看四种摘要。100% 在浏览器中运行,数据绝不离开你的设备。
如需更全面地了解 Web 安全(包括身份验证、输入验证和安全头),请阅读我们的 Web 安全基础指南。
常见问题
MD5 用于校验和还安全吗?
MD5 可以安全地检测意外的文件损坏——如果文件在下载过程中损坏,MD5 哈希值几乎肯定会改变。但 MD5 无法防止蓄意篡改,因为攻击者可以构造具有相同 MD5 哈希值的修改文件。对于需要防范对手的完整性验证(如软件分发),请使用 SHA-256。
SHA-256 比 MD5 慢多少?
在原始吞吐量上,SHA-256 大约比 MD5 慢 2 倍。在现代硬件上,MD5 的哈希速度约为 3,200 MB/s,SHA-256 约为 1,500 MB/s。在实际应用中,这个差距对大多数场景来说可以忽略不计——用 SHA-256 哈希一个 100 MB 的文件大约需要 70 毫秒。只有在超高吞吐量场景(如网络报文检测或大规模存储去重)中,性能差距才值得关注。
我的应用应该用 SHA-256 还是 SHA-512?
大多数应用使用 SHA-256 即可——它提供 128 位碰撞抗性,远超暴力破解能力。选择 SHA-512 的情况包括:(a) 平台为 64 位且需要最大吞吐量;(b) 规范或合规要求强制使用;(c) 需要更大的安全余量以保障长期数据完整性。SHA-512 更长的哈希值会使存储量翻倍,通常并无必要。
SHA-256 能被破解或反推吗?
SHA-256 无法被反推——它是单向函数。截至 2026 年,不存在针对 SHA-256 的实际原像或碰撞攻击。已知最佳攻击需要 2^128 次运算才能找到碰撞,以当前或可预见的技术来看在计算上不可行。SHA-256 经 NIST 认证,被 TLS、Bitcoin 和联邦系统等关键基础设施广泛使用。
既然 MD5 已被破解,为什么有些系统仍在使用?
主要原因是遗留兼容性。许多现有的系统、协议和文件格式在 MD5 弱点被发现之前就围绕它设计。迁移需要所有组件的协调变更。对于缓存键和去重等非安全用途,MD5 的速度优势和更短的输出使其在碰撞攻击不在威胁模型范围内时是一个务实的选择。