摘要
- 只有当接收方先声明自己能够解压哪些算法时,RFC 8879 才允许 TLS 1.3 发送方用
CompressedCertificate代替普通 Certificate 消息。算法声明只是接收能力,不证明压缩真正发生,也不等于给予对端无限资源。 uncompressed_length必须与最终重建字节数完全一致,解压输出不得越过该值;实现还可以采用远低于 TLS 24 位上限的本地限制。这些判断发生在证书能够认证对端之前。- 解压完成不等于验证成功。证据链应分别记录算法声明、实际选择、压缩字节、声明与真实长度、重建消息哈希、解析出的证书链、验证结论、告警、非压缩回退及运输结果。
信任尚未建立,资源边界已经生效
设想一台入口设备同时处理数千次新握手。客户端在 ClientHello 中表示能接收 zlib、Brotli 与 Zstandard 压缩的服务器证书。服务器随后发来很小的 CompressedCertificate,选择 Brotli,却把 uncompressed_length 写成十二兆字节。
真正可能证明服务器身份的 Certificate 消息仍被包在压缩内容里。客户端不能先拿证书的 CA、域名或公钥来决定是否接受这次资源请求。它必须先检查算法是否在自己的清单中,再用普通 Certificate 路径的相同大小限制审查声明值,约束解压器绝不越界,并要求实际输出长度一字节不差。
这就是压缩功能背后的权限结构。发送方可以在接收方给出的算法集合中挑选一种表示;接收方仍决定愿意运行哪些解码器,以及一个尚未认证的连接最多消耗多少内存和 CPU。声明支持 Brotli,不是把内存调度权交给服务器。
TLS 的三字节长度字段可以表示最高 16,777,215 字节,但 RFC 8879 明确允许实现采用更低上限。压缩与未压缩路径必须服从同一限制。如果一份普通 Certificate 因过大而应被拒绝,就不能因为它在线上看起来很小而绕过规则。
声明、选择和成功不能合并计数
compress_certificate 是单向信号。客户端在 ClientHello 中列出自己可用于解压服务器 Certificate 的算法;服务器只有在 CertificateRequest 中列出算法,才表示愿意解压客户端 Certificate。协议没有对应的响应扩展。
发送方可以从收到的列表中选择一种,也可以继续发送普通 Certificate。因此,抓包里出现扩展 27,只能证明接收能力声明,不能证明后续出现了握手类型 25。把“客户端声明支持”当作“压缩握手成功”,是把可能性、执行与结果混为一个指标。
方向也不能推断。客户端能接收压缩的服务器证书,不代表它能发送压缩的客户端证书。库的编译选项与应用配置还可能分别关闭发送或接收。一次成功只能说明该连接、该方向、该算法和该实现路径,而不是系统的对称能力。
该扩展只适用于 TLS 1.3 及以后版本;协商到 TLS 1.2 时应被忽略。它也不是旧式 TLS record compression。后者压缩一般记录并与 CRIME 风险相关;RFC 8879 只改变一个握手消息的表示,重建之后仍回到普通证书处理。
压缩对象是完整编码消息
算法输入不是磁盘上的 PEM 文件,也不是几张独立 X.509 证书。它是原本会发送的完整 TLS Certificate 消息,包括请求上下文、证书列表以及每个 CertificateEntry 的扩展。如果其他扩展改变了消息内容,被压缩的也是改变后的精确字节。
所以审计至少需要两层哈希。第一层标识收到的算法、头部与压缩 payload;第二层标识解压重建后的完整 Certificate 消息。之后再记录叶证书与中间证书指纹,说明普通验证真正检查了哪些对象。
不同算法可以把同一输入变成完全不同的线上字节;同一算法的库版本升级,也可能改变压缩表示而不改变重建结果。只有压缩 payload 哈希,无法跨算法证明逻辑证书消息一致,更无法证明信任链通过。
预压缩缓存同样受这个边界约束。OpenSSL 允许服务器预先压缩已配置证书,而客户端 Certificate 因包含服务器提供的握手专用 context,必须按请求即时生成。服务器缓存也不能只绑定域名:链条续期、中间证书顺序或每项扩展变化,都可能让旧压缩对象不再对应当前消息。
长度字段是门槛,不是成绩
CompressedCertificate 包含算法 ID、24 位的 uncompressed_length 与压缩内容。解码失败、输出超过声明值或最终长度不一致时,接收方必须用 bad_certificate 终止连接。限制应在输出过程中执行,而不是等到内存已经写满才核对。
声明长度有助于提前拒绝和规划 buffer,却不证明对端诚实,也不保证重建内容可安全解析。一个没有越过协议上限的数字,在高并发服务里仍可能过大。本地上限应来自并发、内存与故障实验,而不是简单复制协议字段的最大值。
“压缩后一定更小”也不是有效性条件。BoringSSL 测试特意包含缩小、膨胀与随机转换。协议要求的是在选定算法与边界内精确恢复输入;压缩率属于性能结果。即使输出变大,只要格式与限制正确,也属于该测试要覆盖的路径。
因此应分别观察声明长度、真实长度、峰值分配、解压耗时和错误类型。优秀的比率可能在大并发下造成内存尖峰;并不惊艳的比率,也可能恰好少发一个 packet。单独一个百分比无法证明安全或延迟收益。
解压结束,验证才真正开始
重建成功后,Certificate 必须像未压缩到达时一样接受处理:解析消息、构建证书链、检查名称与时间、执行本地策略,再验证 TLS 1.3 transcript 中的 CertificateVerify。压缩不会修复过期证书、错误域名、未知 CA 或签名失败。
失败原因应保持分层。未声明的算法属于协商错误;越界、解码错误或长度不符属于重建错误;重建消息结构非法属于解析错误;链或名称不成立属于验证错误;CertificateVerify 不正确属于握手认证错误。
把这些都记为“TLS 证书错误”,会丢掉真正采取拒绝动作的控制点。反过来,把 decompression_success 当成认证成功也同样错误:它没有证明预期证书存在,没有证明私钥持有,更没有授予应用交易权限。
运行代码能显示这些界线。BoringSSL 先构造普通 Certificate,记录其精确长度,再调用选定的压缩回调并封装新消息;测试则覆盖错误长度和异常转换。OpenSSL 分别暴露发送、接收、算法偏好与服务器预压缩控制。注册表、库代码和生产应用配置必须逐项取证。
性能结论必须接到完整结果
第一次握手中,证书链往往占用大部分字节。压缩可能减少 records、packets,甚至避免一次运输往返,但结果取决于链条、拥塞窗口、丢包、CPU、缓存和双方共同算法。磁盘 PEM 大小不能代替线上编码消息大小。
测量应同时记录方向与算法声明、实际消息、压缩与原始长度、包与重传、CPU、内存、验证结论和握手完成时间,并使用相同证书身份、相同验证政策的未压缩连接作为对照。
没有共同算法时,发送普通 Certificate 是协议允许的回退。回退率上升可能表示客户端构成变化,也可能是某个 build 丢失了解码器或 TLS 终止层移动。只要两条路径共享限制和验证政策,回退就是表示变化,而不是信任降级。
来源
会员简报
档案背景详情
使用相应会员等级登录,即可解锁完整简报与来源注释。
仅限 Strategic Circle
Strategic Circle
所有读者均可浏览。加入并登录后可解锁档案简报。
加入 Strategic Circle仅限 Leadership Alliance
Leadership Alliance
符合条件的 IP 资产所有者和管理层可登录查看 Leadership Alliance 简报。
加入 Leadership Alliance
