第四章 哈希函数与消息认证码
哈希函数,说白了就是一把数据的「数字指纹」。输入任意长度的消息,输出固定长度的摘要。你想想看,如果两个不同消息算出同一个摘要——那就是碰撞。我的职责就是告诉你:哪些哈希还能用,哪些已经废了。
4.1 哈希函数的核心属性
- 抗原像性:给定摘要,无法反推出原文。
- 抗第二原像性:给定消息,找不到另一条消息使摘要相同。
- 抗碰撞性:找任何两条不同消息碰撞,计算上都不可行。
这三个性质,缺一个就不能用于安全场景。我在项目里见过有人用CRC当哈希,直接就被怼了——那玩意儿防碰撞?不存在的。
实用判断:只要有人说「我们用MD5做安全校验」,你就该立刻警觉。MD5的碰撞攻击已经烂大街了。
4.2 MD5:曾经的王者,现在的过街老鼠
MD5输出128位摘要。原理是四个轮函数,64轮迭代。看起来很复杂对吧?但2004年王小云团队就给出了碰撞。现在用普通笔记本电脑都能在几秒内制造两个不同文件且MD5相同。
我曾经在一家金融公司的日志审计系统里发现,他们用MD5校验日志完整性。我跟他们说,攻击者可以篡改日志后重新计算MD5,你们完全发现不了。后来整个系统重写了。
结论:别再给MD5任何机会。密码存储?不行。文件校验?不行。通信完整性?不行。
4.3 SHA系列:从1到3
| 算法 | 输出长度 | 安全状态 | 我的建议 |
|---|---|---|---|
| SHA-1 | 160位 | 已出现碰撞(SHAttered) | 淘汰 |
| SHA-256 | 256位 | 当前安全 | 首选 |
| SHA-512 | 512位 | 当前安全 | 性能敏感场景慎用 |
| SHA-3 | 可配 | 新一代标准 | 未来替换方向 |
SHA-1的碰撞攻击成本大约11万美元(2017年)。现在肯定更便宜。为什么还要用?我个人习惯,新设计一律用SHA-256。SHA-3采用海绵结构,跟SHA-2完全不同,可以防止长度扩展攻击——这个后面讲HMAC时会提到。
选型口诀:SHA-256打底,SHA-512别乱用(带宽耗不起),SHA-3等生态成熟再上。
4.4 HMAC:给哈希加上密钥
光用哈希做消息认证,容易被重放和长度扩展攻击。HMAC就是把密钥混进哈希计算。公式很简单:HMAC(K, m) = H((K' ⊕ opad) || H((K' ⊕ ipad) || m))。其中ipad和opad是固定填充。
实际开发中,别自己拼凑「哈希+密钥」。我见过有人直接sha256(key + message),结果被长度扩展攻击打得鼻青脸肿。HMAC有双重哈希结构,天然防这个。
// Python HMAC 示例
import hmac, hashlib
key = b'my-secret-key'
msg = b'hello'
digest = hmac.new(key, msg, hashlib.sha256).hexdigest()
print(digest) # 输出64位十六进制字符串
注意:密钥必须足够随机且长度不小于哈希输出长度。我曾经给测试环境配了「123」当HMAC密钥,结果测试同事一眼就看穿了……线上千万别这么干。
4.5 安全注意事项汇总
- MD5、SHA-1 直接拉入黑名单。
- SHA-256/384/512 当前安全,但要注意长度扩展攻击(用HMAC或SHA-3替代)。
- HMAC 使用应保证密钥安全,避免硬编码。
- 哈希不要用于密码存储?得加盐和慢哈希(bcrypt/argon2),但那是另一章内容了。
4.6 小结
哈希函数是密码学的基础元件,但选错了就是灾难。记住:MD5、SHA-1已经「死」了;SHA-256是当前主力;HMAC是认证的正确姿势。下次再有人让你用MD5校验完整性,你可以把这篇内容甩给他。