跳转到内容

哈希函数与消息认证

密码学哈希把任意输入映射成固定长度的指纹。本章讲密码学哈希的三个性质、SHA-2/SHA-3、长度扩展攻击与 HMAC、以及密码存储的慢哈希——先分清它与哈希表用的哈希(→ 哈希表)。

哈希表的哈希密码学哈希
目标快 + 均匀安全性质
抗碰撞不要求必须
抗原像不要求必须
速度越快越好慢有时是特性(密码存储)

MD5/SHA-1 已被碰撞攻击攻破(MD5 2004、SHA-1 2017 实际碰撞):用于签名与证书已不安全;用于非对抗场景(去重、校验)勉强可用——新代码一律 SHA-256 及以上。

  1. 抗原像(preimage):给哈希值 hh,找不到输入 xx 使 H(x)=hH(x) = h——不可逆;
  2. 抗第二原像:给 xx,找不到 x′≠xx' \neq x 使 H(x′)=H(x)H(x') = H(x);
  3. 抗碰撞(collision):找不到任何 x≠x′x \neq x' 使 H(x)=H(x′)H(x) = H(x')。

生日界(birthday bound):nn 位哈希的抗碰撞强度约 2n/22^{n/2}——256 位哈希给出 128 位碰撞强度(→ 公钥密码与密钥交换)。

SHA-2(SHA-256/512)与 SHA-3(Keccak,结构不同——海绵结构)是现代标准;BLAKE3 更快。

Merkle–Damgård 结构(SHA-256 等)的可预测性带来长度扩展攻击:知道 H(k∥m)H(k \| m) 与 mm、∣k∣|k|(不知道 kk),能直接计算 H(k∥m∥padding∥m′)H(k \| m \| \text{padding} \| m')——朴素 MAC(密钥拼在消息前)被攻破:攻击者伪造"密钥认证过"的扩展消息。

HMAC 修复了朴素 MAC 的长度扩展问题:

HMAC(k,m)=H((k⊕opad)∥H((k⊕ipad)∥m))\text{HMAC}(k, m) = H((k \oplus \text{opad}) \| H((k \oplus \text{ipad}) \| m))

双层哈希让攻击者无法利用内部状态。HMAC 是消息认证的标准(TLS、JWT 的 HS256、API 签名):认证用 HMAC,别自己拼密钥。

  • 消息完整性 + 认证:接收方(持同一密钥)验证消息未被篡改且来自持有密钥的一方;
  • 密钥派生:HKDF(基于 HMAC 的 KDF)从主密钥派生子密钥;
  • AEAD 内部:GCM 的 GHASH 与 HMAC 是同一使命(完整性)的不同实现(→ 对称密码)。

存密码不能用快速哈希——SHA-256 太快(GPU 每秒数十亿次),暴力破解与字典攻击毫不费力。密码存储应使用慢哈希(故意慢 + 内存难):

算法特点
bcrypt可调轮数,久经考验
scrypt内存难(抗 GPU/ASIC)
Argon2(2015 竞赛冠军)内存 + 时间都可调,现代默认

三条纪律:

  • 盐(salt):每用户随机盐,防彩虹表(预计算表失效);
  • 慢哈希:Argon2id 调到"登录可接受的最慢"(约 100–500ms);
  • 密码不行就 pepper(服务端全局密钥,存 HSM/KMS)加一层。

密码重置比密码恢复更安全:存不了明文就别想恢复,重置走邮件/OTP。

  • 哈希表的哈希与密码学哈希是两回事:后者必须抗原像与碰撞
  • MD5/SHA-1 已被碰撞攻击攻破;新代码 SHA-256+;生日界 2n/22^{n/2}
  • HMAC 修复了朴素 MAC 的长度扩展问题:认证用 HMAC 别自己拼密钥
  • 存密码不能用快速哈希:Argon2/bcrypt + 每用户盐,调到登录可接受的最慢
  • 密码不可恢复只可重置