哈希算法比较:MD5、SHA、Bcrypt 何时使用?
比较各种哈希算法的特点和用途,提供针对不同场景的选择指南。

什么是哈希函数?
哈希函数是一种将任意大小的数据转换为固定大小唯一值的数学函数。这个转换后的值被称为哈希(Hash)、摘要(Digest)或校验和(Checksum)。哈希函数是现代计算机安全的核心组件,从密码存储到区块链等各个领域都有应用。
为了理解哈希函数,我们用一个比喻:哈希函数就像把肉绞成汉堡肉饼的绞肉机。放入相同的原料总是得到相同的结果,但从肉饼中恢复原来肉的形态是不可能的。这正是哈希函数的核心特性。
哈希函数的核心属性
安全的密码学哈希函数必须具备以下四个基本属性。
1. 确定性 (Deterministic)
相同的输入总是产生相同的输出。没有这个属性,哈希就无法用于数据验证。
// 总是相同的结果
MD5("Hello") === "8b1a9953c4611296a827abf8c47804d7" // true
MD5("Hello") === "8b1a9953c4611296a827abf8c47804d7" // true (永远)
2. 单向性 (One-way)
从哈希值逆推原始数据在计算上必须是不可能的。正是这个属性使哈希能够安全地用于密码存储。即使数据库泄露,仅凭哈希值也无法知道原始密码。
3. 抗碰撞性 (Collision Resistance)
两个不同的输入产生相同哈希值的情况称为碰撞。好的哈希函数必须使找到碰撞变得极其困难。MD5 和 SHA-1 不再被推荐用于安全目的的原因正是因为发现了碰撞。
4. 雪崩效应 (Avalanche Effect)
输入即使只改变1位,输出也必须完全不同。这个属性防止从相似输入中推断哈希值的模式。
MD5("Hello") = "8b1a9953c4611296a827abf8c47804d7"
MD5("hello") = "5d41402abc4b2a76b9719d911017c592"
// 只是大小写一字之差,结果完全不同
主要哈希算法深度分析
MD5:为什么不能再用于安全目的?
MD5 是1991年 Ronald Rivest 设计的128位哈希函数。曾经是使用最广泛的,但现在不应该用于安全目的。
输入: "Hello World"
输出: b10a8db164e0754105b7a99be72e3fe5
长度: 128位 (32字符 hex)
MD5 的衰落: 2004年中国的 Wang Xiaoyun 研究团队在 MD5 中发现了碰撞。之后在2008年,MD5 碰撞被用于生成假 SSL 证书。现在普通计算机在几秒内就能生成 MD5 碰撞。
目前可以使用 MD5 的情况:
- 文件下载完整性检查(用于错误检测而非安全目的)
- 缓存键生成
- 非安全校验和
SHA-1:Google 的 SHAttered 攻击
SHA-1 是由 NSA 设计、NIST 标准化的160位哈希函数。曾被认为比 MD5 更安全,但2017年 Google 和 CWI Amsterdam 通过"SHAttered"攻击演示了碰撞。
在这次攻击中,研究人员生成了两个具有相同 SHA-1 哈希值但内容不同的 PDF 文件。攻击使用的计算量相当于6500年的 CPU 时间和110年的 GPU 时间。虽然需要大量资源,但碰撞被证明是可行的那一刻,SHA-1 的安全可信度就结束了。
当前状态: 大多数浏览器和证书机构拒绝 SHA-1 证书。
SHA-256 (SHA-2):当前的标准
SHA-2 系列是 SHA-1 的后续,有 SHA-224、SHA-256、SHA-384、SHA-512 等多种变体。其中 SHA-256 使用最广泛。
输入: "Hello World"
输出: a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e
长度: 256位 (64字符 hex)
SHA-256 与比特币: 比特币以 SHA-256 为核心。矿工们为了找到使区块头的 SHA-256 哈希值满足特定条件(前面有很多0)的 nonce 值,执行数万亿次哈希计算。SHA-256 的不可预测性使这个过程公平。
用途:
- 数字签名(与 RSA、ECDSA 配合)
- SSL/TLS 证书
- 区块链
- 文件完整性验证
SHA-3:Keccak 的新方法
SHA-3 是2015年 NIST 标准化的最新哈希函数。使用与 SHA-2 完全不同的结构(海绵结构),所以即使在 SHA-2 中发现漏洞,SHA-3 也不受影响。
SHA-3 的设计理念是"做得不同"。SHA-2 使用 Merkle-Damgard 结构,而 SHA-3 的 Keccak 算法基于海绵函数。这是一种保险。SHA-2 目前仍然安全,但为未来的攻击准备了备选方案。
Bcrypt:专为密码设计的特殊哈希
Bcrypt 与通用哈希不同,是故意设计得很慢的。在密码哈希中,"慢"是优点。因为它阻止攻击者每秒尝试数十亿个密码。
// 使用工作因子(cost factor) 12
bcrypt.hash("password", 12)
// 输出示例
$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/X4.VVzfQl0Pfy6.nO
Bcrypt 哈希值结构:
$2b$- 算法版本12$- 工作因子 (2^12 = 4,096次迭代)- 接下来22字符 - Salt (Base64 编码)
- 其余 - 实际哈希值
工作因子每增加1,计算时间翻倍。当前推荐值是10-12。
Argon2:最新密码哈希
Argon2 是2015年 Password Hashing Competition 的冠军算法。设计为内存密集型,对 GPU 并行攻击具有抵抗力。
参数:
- 内存: 使用多少 RAM
- 时间: 计算多长时间
- 并行度: 使用几个线程
Argon2id 变体推荐用于一般用途。新项目使用 Argon2,需要向后兼容时使用 Bcrypt。
实战应用指南
密码存储:Salt 和 Pepper
Salt: 为每个密码添加唯一的随机值。即使相同的密码也会生成不同的哈希值,从而使彩虹表攻击失效。
// Salt 使用
password: "password123"
salt: "x7Gh9kL2" (随机生成)
hash: bcrypt("password123" + "x7Gh9kL2")
Pepper: Salt 与哈希一起存储在数据库中,而 Pepper 作为单独的秘密管理(环境变量、硬件安全模块等)。即使数据库泄露,没有 Pepper 也无法验证密码。
文件完整性验证
软件发布时同时提供 SHA-256 校验和,可以确认下载的文件未被篡改。
# 生成文件哈希
sha256sum ubuntu-22.04.iso
# a1b2c3d4e5f6... ubuntu-22.04.iso
# 验证
echo "a1b2c3d4e5f6... ubuntu-22.04.iso" | sha256sum --check
按用途选择算法指南
| 用途 | 推荐算法 | 不推荐 |
|---|---|---|
| 密码存储 | Argon2id, Bcrypt, PBKDF2 | MD5, SHA 系列 (太快) |
| 文件完整性 | SHA-256, SHA-512 | MD5 (可能碰撞) |
| 数字签名 | SHA-256 + RSA/ECDSA | SHA-1 (碰撞攻击) |
| API 令牌 | HMAC-SHA256 | - |
| 缓存键 | MD5, SHA-1 (快) | - |
代码示例
Node.js
const crypto = require('crypto');
const bcrypt = require('bcrypt');
// SHA-256
const sha256 = crypto.createHash('sha256')
.update('Hello World')
.digest('hex');
// Bcrypt
const hash = await bcrypt.hash('password', 12);
const isValid = await bcrypt.compare('password', hash);
Python
import hashlib
import bcrypt
# SHA-256
sha256 = hashlib.sha256(b'Hello World').hexdigest()
# Bcrypt
hashed = bcrypt.hashpw(b'password', bcrypt.gensalt(12))
is_valid = bcrypt.checkpw(b'password', hashed)
Toolypet Hash Tools
使用 Toolypet 的哈希工具可以快速生成和验证各种哈希:
- MD5/SHA Generator: 即时计算文本或文件的哈希值
- Bcrypt Tool: 密码哈希生成和验证
- Hash Comparison: 安全比较两个哈希值(防止时序攻击)
在 Toolypet 轻松处理开发和测试所需的哈希工作。