本节摘要:SOURCE 2.3:SHA-256 为常用安全哈希;MD5 已破,勿用于安全场景。哈希用于完整性、证书指纹、HMAC 与数字签名预处理。
运维文档写「对比 MD5 指纹」,攻击者可构造 MD5 碰撞证书。SOURCE 明确 MD5 不适合安全用途;应改用 SHA-256 指纹:
openssl x509 -in server.crt -noout -fingerprint -sha256
| 特性 | 含义 |
|---|---|
| 单向性 | 从哈希难反推原文 |
| 抗碰撞 | 难找两段不同输入同哈希 |
| 固定长度 | SHA-256 输出 256 bit |

⚠️ 常见坑:用 MD5 校验下载文件完整性——碰撞攻击可伪造同哈希恶意文件。
💡 关键直觉:哈希不提供机密性;它回答「有没有被改过」。
哈希函数回答的是「数据有没有被改过」,但它的使用方式决定了回答的可信度。裸哈希(比如下载页给一个 SHA-256 值)只能防「无意的传输损坏」,因为攻击者可以同时替换文件和哈希值;要防「有意的篡改」,必须借助带密钥的机制——HMAC 或数字签名。HMAC 用共享密钥参与运算,只有双方能生成与验证;数字签名用私钥签名,任何人都能验证来源。三者的差异归结为「信任谁」。
在 TLS 里哈希的角色更为复杂:证书签名前要对证书内容做哈希;握手过程的 transcript 要被哈希后放进 Finished 消息,防止握手消息被篡改;密钥派生阶段要用哈希驱动的 HKDF 展开出多组会话密钥。因此哈希函数不仅是完整性工具,还是整个密码协议「把随机性安全地变成密钥」的基石,这正是 SHA-256 在 TLS 1.3 中贯穿始终的原因。
# 完整性验证的三种信任模型 方式 防什么 信任前提 典型应用 裸哈希 传输错误 校验值渠道可信 下载完整性提示 HMAC 篡改 双方共享密钥 会话 MAC、API 认证 数字签名 篡改 + 伪造 签名者私钥安全 证书、代码签名
工程判断要点:凡是「攻击者可能同时改文件与校验值」的场景,必须用带密钥或签名的机制;凡是「防无意损坏」的场景,裸哈希就够。证书指纹、固件校验、下载校验这些不同场景,对应选择 SHA-256 指纹、签名或 HMAC,选错信任模型是完整性体系最常见的失效方式。
# 哈希算法状态对照 算法 输出长度 安全状态 适用场景 MD5 128 bit 已破(碰撞) 仅遗留兼容,禁止新用 SHA-1 160 bit 签名场景已淘汰 仅旧证书验签兼容 SHA-224 224 bit 可用但少用 特宽场景 SHA-256 256 bit 安全,主流 证书、指纹、HMAC SHA-384 384 bit 安全 高安全级别 SHA-512 512 bit 安全 FIPS 兼容场景 SHA3-256 256 bit 安全(新一代) 新设计可选用 BLAKE2b 任意 安全 文件校验、协议内部 # 常见哈希用途与命令 openssl dgst -sha256 file.bin # 计算文件 SHA-256 openssl x509 -noout -fingerprint -sha256 -in cert.pem # 证书 SHA-256 指纹 echo -n "data" | openssl dgst -sha256 # 文本摘要 openssl dgst -sha256 -hmac "secret" -in file.bin # HMAC 计算(带密钥) # 选择完整性与认证机制的决策 需要防什么 用什么 只防传输损坏 裸 SHA-256(校验值另行保护) 双方共享密钥下防篡改 HMAC-SHA-256 公开环境下防伪造 + 溯源 数字签名(ECDSA/RSA-PSS) 口令存储 加盐哈希 bcrypt/argon2(非 SHA)
口令存储这一行特别提示:口令绝不能用裸 SHA 存储,必须用慢哈希加盐。哈希函数家族庞大,但只要按「防什么」选择机制,就基本不会选错。
再补充哈希在实际开发中常见的两个误用场景。一是用哈希当加密用:有人把敏感字段哈希后存起来,以为就「加密」了,但哈希没有密钥,字典与彩虹表可以直接还原常见值,正确做法仍是带密钥的加密方案。二是混淆「防篡改」与「防伪造」:校验下载文件时只给一个 SHA-256 值,攻击者把文件和值一起换掉就失效了,必须用签名或 HMAC 才能防住有意的替换。把握住「哈希无密钥、不提供机密性」这两个本质,误用就能基本避免。
开发中最实用的习惯是:完整性问题先问一句「校验值从哪里来、可信吗」,口令存储先问一句「是否用了慢哈希加盐」。两个问题问清楚,绝大多数哈希相关漏洞都能在设计阶段被挡下。