哈希算法与消息摘要
说起哈希算法,我脑子里第一个蹦出来的场景就是:几年前做支付系统,需要校验订单数据的完整性。那时候MD5还在用,后来被安全审计揪出来,说这玩意儿早就该退役了。嗯,从那以后我养成了个习惯——但凡涉及「不可逆」和「防篡改」的地方,一定先把算法族谱翻一遍再下决定。
哈希函数,说白了就是一把压缩器。你给任意长度的数据,它吐出一个固定长度的“指纹”。这个指纹有几个关键特征:
- 单向性:从指纹反推出原文,理论上不可行(实际上MD5已经可以碰撞了)。
- 抗碰撞性:找到两个不同输入产生相同输出,应该极难。
- 雪崩效应:输入改一个比特,输出几乎完全不同。
下面这张图把今天要聊的几个主流哈希算法串起来了。你把它当成一张路线图,读完后回来再看,脉络会更清楚。
MD5:曾经的主力,如今的雷区
MD5输出128位,速度快,实现简单。十年前我接手过一个老系统,密码存储就是简单MD5。那时候攻击者还没把彩虹表玩得那么溜,勉强能扛。2010年后碰撞攻击被大幅优化,现在用手机都能在几分钟内找到两个不同文件产生相同MD5值。
我个人的态度很明确:任何新项目都不应该再用MD5。如果你看到代码里还有MD5做签名或者存密码,趁早改。我曾经在一个项目里见到过MD5加盐的方案,表面看安全一些,但实际上盐值一旦泄露,MD5的弱点依然能被直接利用。
SHA-1:退休进行时
SHA-1输出160位,比MD5稍慢,但安全强度其实也没好到哪里去。2017年Google和CWI成功给出了第一个完整SHA-1碰撞实例(SHAttered攻击)。从那以后,浏览器证书、Git存储等纷纷弃用它。
我记得有一次给客户做系统的密码升级,老的哈希字段就是SHA-1。我们做了渐进迁移——用户登录时如果检测到是SHA-1,就重算SHA-256并更新。整个过程花了一个月,但之后再也没有安全告警了。
建议: 除非你必须维护一个上古组件,否则别碰SHA-1。Chrome已经明确标记SHA-1证书为不安全,这已经足够说明问题了。
SHA-2:当前工业标准
SHA-2家族包括SHA-224、SHA-256、SHA-384、SHA-512。其中SHA-256是主流选择。输出长度越长,安全性越高,速度也越慢。实际项目中,我推荐对称选择:
| 算法 | 输出长度(位) | 安全等级 | 典型场景 |
|---|---|---|---|
| SHA-224 | 224 | 中 | 兼容性要求极低的环境 |
| SHA-256 | 256 | 高 | 证书签名、文件校验、密码存储(加盐) |
| SHA-384 | 384 | 更高 | 金融、政务等强合规场景 |
| SHA-512 | 512 | 极高 | 超长摘要需求(但因效率常被SHA-384替代) |
我自己的经验:如果项目没有特殊合规要求,直接上SHA-256。速度和安全性之间平衡得很好。Java 8以上的 MessageDigest 原生支持,用起来很方便。
// Java 示例:计算字符串的 SHA-256 摘要
import java.security.MessageDigest;
public class HashDemo {
public static void main(String[] args) throws Exception {
String input = "Hello, 加密SDK实战!";
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] digest = md.digest(input.getBytes("UTF-8"));
// 转成十六进制字符串
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
sb.append(String.format("%02x", b & 0xff));
}
System.out.println(sb.toString());
}
}
digest(InputStream, byte[]) 方法配合缓冲区,避免一次性加载整个文件到内存。我曾经处理过一个10GB的日志文件,用流式读取每次1MB缓冲区,SHA-256只用了不到5秒。
SHA-3:新一代选择
SHA-3基于Keccak算法思想,于2015年被NIST标准化。它和SHA-2结构完全不同,不是加固版本,而是另一种构造方式。输出长度可灵活配置(224/256/384/512)。目前SHA-3还没有大规模替代SHA-2,因为前者的性能在某些硬件上没有优势,且SHA-2短时间内仍然安全。
我个人觉得,如果项目从头搭建,且需要应对未来十年以上的安全需求,可以优先选SHA-3-256。它在量子计算时代也不容易被打穿,当然这只是保守推测。不过我在实际项目中见到SHA-3用得还比较少——大多数团队还是习惯用SHA-256。
消息摘要的几个实战要点
- 加盐存储密码: 直接对密码哈希会遭受彩虹表攻击。正确的做法是附加随机盐值,再计算哈希。盐值长度不少于16字节,且每个用户不同。
- HMAC(哈希消息认证码): 使用另一个密钥和哈希函数组合,保证消息完整性和来源认证。我常用来做API请求签名。
- 防重复提交: 可以用哈希作为请求的唯一指纹,服务器缓存该指纹一段时间。
- 文件重复检测: 大型文件系统里,对文件内容做哈希,相同哈希代表内容相同(不考虑碰撞)。
- MD5、SHA-1已过时,严禁用于安全场景。
- SHA-256是当前性价比最高的选择。
- SHA-3是未来储备,推荐新项目考虑。
- 无论用哪种哈希,密码存储一定要加盐,并且推荐使用 bcrypt/Argon2 这类专门算法。
嗯,关于哈希我能想到的坑和经验差不多就这些了。你在写代码时遇到的具体问题,欢迎去课程讨论区里提,我看到会回复。