对称加密模式:ECB、CBC、CTR 模式及其安全性
对称加密算法,你想想看,其实就像一把钥匙开一把锁。但光有算法还不够,还得有一套规矩,规定怎么把数据切成块、怎么加密、怎么串联。这套规矩,就叫「加密模式」。我刚开始接触的时候也犯过迷糊,觉得算法选对了就行,模式选个默认的得了。结果呢?踩了个大坑。
本章咱们就聊透三种最经典的对称加密模式:ECB、CBC 和 CTR。我把这些年项目里亲身体验过的坑和技巧都掏出来,帮你一次性理清。
为什么要有加密模式?
AES、DES 这类分组加密算法,一次只能加密一个固定长度的块(比如 128 位)。那如果你的数据超过 128 位怎么办?或者不到 128 位怎么办?
加密模式就是来解决这两个问题的:
- 分块:把长数据切成若干小块,挨个加密
- 填充:最后一块如果不够长,补足到块大小
不同的模式,处理方式天差地别。安全性也完全不同。
ECB 模式:简单但危险
ECB(电子密码本模式)最直观。你把数据分成 128 位的块,每块用同一把密钥独立加密。加密结果也是一块一块的,拼起来就是密文。
说白了,ECB 就是分组加密里的「照片墙」——每个像素块之间没有任何关联,各自独立上色。
哪里不安全?
ECB 最大的问题——确定性。相同的明文块,加密后总是得到相同的密文块。你想想看,如果密码本的正文位置固定了,那密文也就固定了。
我做安全审计时遇到过真实案例。一个系统用 ECB 加密用户头像图片——头像的轮廓完全能从密文中辨认出来。虽然数据本身是加密的,但图案信息全暴露了。这就是 ECB 的典型问题。
ECB 的适用场景
| 场景 | 是否推荐 | 原因 |
|---|---|---|
| 加密小于一个分块的随机密钥 | ✅ 可以 | 数据量小,且是随机值,不存在模式泄露 |
| 加密大于一个分块的数据 | ❌ 禁止 | 密文会泄露明文的结构信息 |
| 加密图片、视频等结构化数据 | ❌ 禁止 | 密文会保留图像的轮廓和颜色分布 |
CBC 模式:链式依赖,安全多了
CBC(密码分组链接模式)解决了 ECB 的问题。它的核心思想:让每个密文块都依赖前一个密文块。
具体怎么做的?加密第一个块之前,先用一个随机值(IV,初始化向量)跟明文做 XOR 运算,再加密。后面的块,先跟前一个密文块做 XOR,再加密。
这样,相同的明文块在明文不同位置,得到的密文也不同。你想想看,图片的密文就不会再显示出轮廓了。
CBC 的关键点
- IV 必须随机:每次加密都要生成一个新的随机 IV。IV 可以公开,但不能重复。
- 不能并行加密:因为每个块都依赖前一个密文,加密没法并行。解密倒是可以并行。
- 填充问题:最后一块需要填充,我过去遇到过因为填充校验逻辑不严格导致的「填充预言攻击」。这个坑在后面章节会细讲。
CTR 模式:流式加密,高效灵活
CTR(计数器模式)是另一种思路。它不把加密算法直接用在明文上,而是先用密钥加密一个不断递增的计数器(counter),生成一个密钥流,再用这个密钥流跟明文做 XOR。
这有点像流密码——加密和解密完全对称,而且可以并行。
CTR 的优势
- 可并行:加密和解密都能并行,性能极好。我有一回做视频流加密,CTR 比 CBC 快了 3 倍多。
- 随机访问:可以解密任意位置的密文块,不用从头开始。这对加密数据库的索引字段特别有用。
- 不需要填充:直接按字节生成密钥流,短数据不需要凑成整块。
CTR 的坑
CTR 模式有一个致命弱点:计数器绝不能重复。如果两次加密用了相同的计数器和相同的密钥,那这两个密钥流就一样了。得到两个密文后,通过 XOR 就能直接消除密钥,暴露出两个明文的差值。
我曾经在服务器端落地 CTR 模式,差点踩到这个坑。多亏老同事提醒,我才发现配置里计数器没做好管理。
三种模式对比速览
| 特性 | ECB | CBC | CTR |
|---|---|---|---|
| 相同明文是否产生相同密文 | 是 | 否(取决于 IV) | 否(取决于计数器) |
| 加密能否并行 | 能 | 不能 | 能 |
| 解密能否并行 | 能 | 能 | 能 |
| 是否需要填充 | 是 | 是 | 不需要 |
| 安全风险 | 明文结构泄露 | 填充预言攻击 | 计数器重用 |
| 推荐使用场景 | 几乎没有 | 通用加密,需注意 IV | 高并发、流式数据 |
本章知识结构
下面这张图可以帮你把三种模式的逻辑关系理清。
避坑指南
这三种模式我都在项目中实测过,踩过的坑总结一下:
- ECB 的坑:加密图片或结构化数据时,密文依然能看出明文的结构。我曾经见过一个系统,直接拿 ECB 加密用户头像——结果头像轮廓全暴露了。
- CBC 的坑:IV 必须每次加密都随机生成。我曾经给一个项目做审计,发现所有加密数据的 IV 都是全 0——整个加密体系形同虚设。
- CTR 的坑:计数器管理失败会导致密钥流重复。我之前在服务器端差点踩到这个坑,多亏代码审查时发现计数器状态没做持久化。