填充预言是什么
填充预言(padding oracle)指的是一种可被外部观测的判定差异:服务端解密失败时,把「填充错误」和「消息认证失败」区分开来反馈(错误码、页面内容、响应耗时不同),攻击者据此每次确定一个字节,最终逐字节还原明文。它不是算法漏洞,而是使用方式泄漏信息,典型出现在 CBC 加解密的接口与早期 TLS 实现上。
基本信息
| 成因 | 解密错误被分类暴露:错误码、页面、耗时差异 |
|---|---|
| 典型场景 | CBC + MAC 的 TLS 1.2 及更早、自研 Cookie/Token 加密 |
| 受影响算法 | CBC 类分组模式(含 AES-CBC、3DES-CBC) |
| 不受影响 | AEAD(GCM、ChaCha20-Poly1305、CCM) |
| 根治手段 | 改用 AEAD 并统一错误响应与处理耗时 |
| 缓解手段 | Encrypt-then-MAC(RFC 7366)并保持常量时间处理 |
典型使用场景
- TLS 服务端梳理:把 CBC 套件从配置中移除,全面转向 AEAD
- 自研加密接口整改:登录态、优惠券、下载令牌等被加密后交给客户端的字段
- 代码审计:查找「先解密再校验格式」的写法,这类结构天然存在错误分类
- 渗透测试复盘:把已披露的填充预言类漏洞映射到自身代码的同类模式
- 遗留系统加固:无法立即换算法时,先消除错误分类与耗时差异
服务端只保留 AEAD 套件
服务端只保留 AEAD 套件
# TLS 1.2 侧只留 ECDHE 与 AEAD 组合,CBC 全部去掉
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers on;
# 验证:确认协商结果里没有 CBC
openssl s_client -connect example.com:443 -tls1_2 </dev/null 2>/dev/null | grep 'Cipher'应用层防护要点
应用层防护要点
# 反面写法:解密失败与校验失败返回不同状态码,攻击者可区分
if not padding_ok(data): return 400 # 泄漏点:填充错误
if not mac_ok(data): return 401 # 泄漏点:认证失败
# 正面写法:统一出口,且不把异常细节写进响应
ok, plain = try_decrypt_and_verify(data)
if not ok:
log_internal_reason() # 细节只进内部日志
return same_error_response() # 对外只有一种错误
# 另外:比较 MAC 用常量时间函数,避免耗时差异成为新的判定通道旁路与对策对照
| 旁路形式 | 攻击者能观测到什么 | 对策 |
|---|---|---|
| 错误码不一致 | 填充错误返回 400、认证失败返回 401 | 对外统一为一种错误码 |
| 页面内容差异 | 不同错误返回不同页面或字段 | 统一响应体,去掉差异化文案 |
| 响应耗时差异 | 填充校验与 MAC 校验的处理时间不同 | 改用常量时间比较,必要时统一拒绝路径 |
| 连接行为差异 | 提前关闭连接或发送不同告警 | 保持一致的连接处理与告警策略 |
| 日志与监控差异 | 外部可触发不同告警类别 | 只在内部记录细节,不外泄分类信息 |
只改错误文案往往不够:加解密顺序、MAC 覆盖范围与比较方式都会留下可观测差异。真正的根治是改用 AEAD,让「解密失败」只有一种可能;在无法换算法的存量系统里,至少要同时消除错误分类、耗时差异与连接行为差异。
Encrypt-then-MAC(先加密再对密文做 MAC)比 MAC-then-Encrypt 更安全:接收方可以先验 MAC 再解密,MAC 不通过就统一拒绝,从根本上避免进入「解密后才发现填充错误」的路径。TLS 的 Encrypt-then-MAC 扩展即为此提供(RFC 7366)。