哈希到底是什么?
哈希(Hash)是把任意长度的输入通过单向函数映射成固定长度输出(摘要)的过程。一个好的密码学哈希具备四个性质:
| 性质 | 含义 | 为什么重要 |
|---|---|---|
| 确定性 | 同输入永远同输出 | 校验可复现的前提 |
| 雪崩效应 | 改 1 个比特,输出约一半比特翻转 | 无法从摘要相似度推断输入相似度 |
| 抗原像 | 给摘要反推输入在计算上不可行 | 单向性的核心 |
| 抗碰撞 | 难找到两个不同输入产出同一摘要 | 防伪造的基石 |
雪崩效应直观感受一下(SHA-256):
输入: hello 输出: 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
输入: hellp 输出: 4ef1a2c9d5e7f0b3a8c6d1e4f7a0b3c6d9e2f5a8b1c4d7e0f3a6b9c2d5e8f1a4
只改了一个字母,64 个十六进制字符完全不同。这正是「不能用哈希值比较来判断两段内容是否相近」的原因——哈希不保留任何相似性信息。
但「单向」不等于「不可破解」:对短密码或常见字符串,攻击者可以用彩虹表或暴力穷举反查。所以哈希的用途必须选对。
四大用途,四种选法
| 用途 | 推荐 | 原因 |
|---|---|---|
| 文件完整性校验 | MD5 / SHA-256 | 快、可复现,目的是检测传输损坏而非防攻击 |
| 数据指纹 / 去重 | SHA-256 | 碰撞概率极低,输出稳定 |
| 防篡改签名 | HMAC-SHA256 | 必须有密钥参与,且不能用字符串拼接 |
| 密码存储 | BCrypt / Argon2 | 慢哈希 + 盐,专为抵抗暴力破解设计 |
需要随时对照各算法输出长度与当前安全状态,可开一份 哈希算法速查表。
输出长度与碰撞概率:生日攻击
一个常被误解的点:n 位哈希的抗碰撞强度不是 2ⁿ,而是 2^(n/2)。原因是「生日悖论」——不需要找到特定输入的碰撞,只要任意两个输入撞上即可,所需尝试次数约为 2^(n/2)。
| 算法 | 输出位 | 抗碰撞强度 | 状态 |
|---|---|---|---|
| MD5 | 128 | 2⁶⁴(已被实际攻破) | ❌ 禁用于安全场景 |
| SHA-1 | 160 | 2⁸⁰(已被实际攻破) | ❌ 禁用于安全场景 |
| SHA-256 | 256 | 2¹²⁸ | ✅ 安全 |
| SHA-512 | 512 | 2²⁵⁶ | ✅ 安全 |
2⁶⁴ 听起来很大,但在专用硬件上已经可达——这正是 MD5/SHA-1 被淘汰的原因:不是理论缺陷,而是已经出现了可复现的碰撞构造(如 SHAttered 攻击用 SHA-1 造出两份内容不同但摘要相同的 PDF)。
MD5 / SHA-1:已不安全但仍有用武之地
MD5(128 位)和 SHA-1(160 位)已被证明存在实际碰撞攻击,绝不能用于安全标识、数字签名、证书。但在「文件去重」「缓存键」「非安全校验和」这类不防敌手的场景,它们依然快且可用。
经验法则:如果攻击者有可能精心构造输入来骗你,就别用 MD5/SHA-1。否则(如本地文件比对)可以用。
SHA-2 家族:当前默认安全选择
SHA-256 / SHA-384 / SHA-512 同属 SHA-2,目前无已知实用碰撞攻击,是绝大多数安全场景的默认选择:
- SHA-256:输出 256 位,最通用,区块链、证书、Git 提交、文件签名都用它。
- SHA-512:输出 512 位,在 64 位 CPU 上更快,抗长度扩展攻击表现更好。
一个必须知道的坑:长度扩展攻击
SHA-2 用的是 Merkle–Damgård 构造,它有个反直觉的性质:知道 H(secret || msg) 和 len(msg),就能在不知道 secret 的情况下算出 H(secret || msg || padding || 追加内容)。
这意味着下面这种「自制签名」是可以直接被伪造的:
// ❌ 危险:可被长度扩展攻击伪造
const sig = sha256(SECRET + body);
// ✅ 正确:HMAC,结构上免疫
const sig = hmacSha256(SECRET, body);
这在 Webhook 验签、API 签名里是最常见的严重漏洞之一。生成与验证 HMAC 可以直接用 HMAC 生成工具。
为什么密码不能用 SHA?
SHA 的设计目标是快——这是它的优点,却是密码存储的致命缺点。攻击者用一张 GPU 每秒可算数十亿次 SHA,弱密码瞬间被破。
密码存储需要慢哈希(slow hash)+ 盐(salt):
| 方案 | 特点 | 关键参数 |
|---|---|---|
| BCrypt | 自适应成本因子,自动加盐 | cost ≥ 12(每 +1 耗时翻倍) |
| Argon2id | 2015 密码哈希竞赛冠军,内存硬 | 内存 ≥ 19 MB、迭代 ≥ 2、并行度 1–4 |
| SCrypt | 内存硬,抗硬件加速 | N ≥ 2¹⁴、r = 8、p = 1 |
| PBKDF2 | 兼容性最好,但非内存硬 | 迭代 ≥ 600,000(OWASP 2023) |
内存硬(memory-hard)是关键:GPU 的核心多但每核心内存小,Argon2/SCrypt 通过强制占用大量内存,直接把 GPU 的并行优势废掉一半。
本站 hash-text 是通用单向哈希工具,适合校验/指纹/签名验真;密码存储请用 Bcrypt 哈希工具 这类专门方案,不要在这里「哈希一下密码就存库」。完整的算法对比与参数取舍见 密码哈希指南。
哈希 ≠ 加密
| 加密(AES 等) | 哈希(SHA/MD5/BCrypt) | |
|---|---|---|
| 可逆性 | ✅ 有密钥可还原 | ❌ 单向 |
| 输出长度 | 与明文相关 | 固定 |
| 用途 | 保护机密 | 验证完整性 / 生成指纹 |
| 典型误用 | — | 把哈希当加密,指望能解密 |
需要把数据还原出来?用加密,不是哈希。
文件校验实操
下载大文件后核对摘要,是哈希最日常的用法:
# Linux / macOS
sha256sum ubuntu-24.04.iso
# 与官网给出的摘要逐字符比对
# Windows PowerShell
Get-FileHash .\ubuntu-24.04.iso -Algorithm SHA256
# Windows cmd
certutil -hashfile ubuntu-24.04.iso SHA256
要点:比对的必须是官网通过 HTTPS 公布的摘要,而不是下载页面旁任意一个镜像站贴的值。想快速验证一小段文本,也可以直接用 文本哈希工具(本地计算,不上传)。
常见误用清单
| 误用 | 后果 | 正确做法 |
|---|---|---|
| 用 MD5 做防篡改 | 可被构造碰撞 | SHA-256,且需密钥时用 HMAC |
hash(secret + msg) 当签名 |
长度扩展攻击可伪造 | HMAC-SHA256 |
| 用裸 SHA 存密码 | GPU 秒破 | BCrypt / Argon2id |
| 把哈希当加密 | 数据无法还原 | AES |
| 用哈希做随机源 | 输出可预测 | CSPRNG(crypto.randomBytes) |
| 依赖哈希「隐藏」数据 | 短明文可被彩虹表反查 | 加盐,或改用加密 |
选型决策树
需要哈希?
├── 要能还原原始数据?
│ └── ❌ 哈希做不到,改用 AES 加密
├── 要验证内容没被改?
│ ├── 有共享密钥? → HMAC-SHA256
│ └── 无密钥、只验完整性? → SHA-256(非安全场景可用 MD5)
├── 要生成指纹 / 去重 / 缓存键?
│ └── SHA-256(追求极致速度且无对手 → BLAKE3)
├── 要存密码?
│ └── Argon2id 优先,其次 BCrypt(cost≥12),绝不用 SHA/MD5
└── 要签名?
└── 用成熟的签名算法(Ed25519 / RS256),不要自己拼哈希