3. 踩坑二:加密算法选择不当 - 使用已淘汰算法

这个问题说起来挺尴尬的。我在一次代码审计中,发现某个核心业务字段居然还在用 DES 加密。问团队为什么选这个,回答是「以前一直这么用的,没出过事」。嗯,这句话本身就是最大的风险。

加密算法选错了,就等于你家大门装了一把三十年前的锁。锁没坏,但小偷手里的工具早就升级了。今天我们就来盘一盘,哪些算法已经不能用了,以及你为什么不能随手复制网上的老代码。

3.1 已淘汰算法清单

我个人习惯把算法分为三档:禁用、不推荐、安全。下面这张表可以直接贴在团队的 Wiki 里。

分类 算法 密钥长度 淘汰原因 替代方案
禁用 DES 56 位 穷举攻击可在 24 小时内破解 AES-256
禁用 MD2 / MD4 / MD5 —— 碰撞攻击成本极低(2004 年已演示) SHA-256 / SHA-3
禁用 RC4 可变 偏差暴露明文,TLS 已禁用 ChaCha20 / AES-GCM
不推荐 3DES (TDEA) 112 位有效 速度慢,Sweet32 攻击可用 AES-256
不推荐 RSA-1024 1024 位 NIST 建议 2048 位以上 RSA-3072 / ECDSA
不推荐 SHA-1 160 位 Google 已展示 SHAttered 碰撞 SHA-256 / SHA-384
⚠️ 警告: 千万别觉得「3DES 比 DES 多了两轮就安全」。我见过一个项目把 3DES 当金标准,结果被 Sweet32 攻击打得措手不及。说白了,大部分已淘汰算法的缺陷都是公开的 CVE,不是理论问题,是实战问题。

3.2 为什么人们还在用已淘汰算法?

我在项目中遇到过三次类似情况,原因惊人一致:

  • 历史遗留 —— 数据库里存的老密文没法一次性迁移,新老代码混着跑。
  • 「能用就别动」心态 —— 产品经理说改密码算法要停服,不敢改。
  • 复制粘贴灾难 —— 从 Stack Overflow 或者十年前的博客直接抄代码,没看日期。

你想想看,2017 年之后开源的加密库基本都把 DES 和 MD5 标为废弃了。如果你还在用,要么是没升级库,要么是主动绕过了警告。后者更危险——我在代码审查里见过有人特意用 @SuppressWarnings 压掉 IDE 的过期提醒。这不是在解决问题,是在埋雷。

3.3 字段级加密的算法选型原则

字段级加密跟传输层加密不一样。传输层可以用临时密钥协商,字段加密往往是静态的,密文要存很久。所以选算法时,我一般会问自己三个问题:

  1. 未来五年会不会被破解? —— 选 AES-256 或 ChaCha20,当前没有已知有效攻击。
  2. 有没有行业标准背书? —— 比如 FIPS 140-3 认证的算法,或者 NIST 推荐。
  3. 密钥怎么管理? —— 算法再强,密钥硬编码在代码里也是白搭。这个后面会单独讲。
💡 我的习惯: 对于字段级对称加密,直接锁定 AES-256-GCM。记得加关联数据(AAD),防止篡改。别用 ECB 模式——它会把相同的明文块加密成相同的密文块,等于把结构暴露给攻击者。

3.4 代码示例:从 MD5 迁移到 SHA-256

假设你原来用 MD5 哈希银行卡号(别笑,我真见过)。现在的做法应该是 HMAC-SHA256 加盐。下面是伪代码思路:

// ❌ 旧做法:MD5 裸哈希(不要用!)
String hash = DigestUtils.md5Hex(cardNumber);
// 问题:无盐,可查彩虹表,碰撞极易构造

// ✅ 新做法:HMAC-SHA256 + 随机盐
byte[] salt = generateRandomSalt(16);  
// 每个字段单独盐,不可复用
Mac hmac = Mac.getInstance("HmacSHA256");
SecretKeySpec key = new SecretKeySpec(masterKey, "HmacSHA256");
hmac.init(key);
byte[] hashBytes = hmac.doFinal(cardNumber.getBytes(StandardCharsets.UTF_8));
String hash = Base64.getEncoder().encodeToString(hashBytes);
// 存储时把盐也保存(明文),用于验证权重

这里有个坑:盐必须随机且独立。我曾经看到有人把系统启动时间当盐……同一个时间段写入的数据全部一个盐,那跟没加没什么区别。

3.5 一张图理清算法选择逻辑

下面这张流程图画的是我个人做算法决策时的判断路径。每次选加密方案,我都会走一遍。

字段级加密算法选型流程图 选择加密算法 是需要可逆加密(如手机号、身份证) → 选择 AES-256-GCM / ChaCha20 是不可逆哈希(如密码、脱敏ID) → 使用 HMAC-SHA256 + 随机盐 避免:DES/3DES/MD5/SHA-1/RC4 不要用:ECB模式 / 固定IV / 硬编码密钥 ✅ 最终选择:AES-256-GCM / HMAC-SHA256 ⚠️ 注意 1. GCM 模式自带认证,不需要额外做 HMAC 2. 密钥必须由 KMS / HSM 管理,不使用代码硬编码 3. 定期轮换密钥,并支持双读双写过渡

3.6 避坑指南

  • 不要迷信「自定义加密算法」 —— 我见过一个团队自己发明了一个异或+移位算法,号称比 AES 快。结果内部测试不到 10 分钟就被我同事用线性分析推出来了。自己写密码学等于把自己当全世界的攻击者的靶子。
  • 不要用同一个密钥加密所有字段 —— 如果每个字段都使用不同的派生密钥(通过主密钥 + 字段标识 + 盐生成),一个字段泄露不会波及其他字段。
  • 曾经有个项目把 RSA 私钥明文写在 GitHub 上 —— 虽然这是配置管理的问题,但如果你选错了算法,连被破解的机会都省了。
总结一句话: 算法选型不是靠记忆,是靠清单。每次项目开始前,拿上面那张表过一遍,比事后修漏洞省太多成本。