摘要

  • 206 成功答复的 CRLF 一结束,双向压缩立即生效,并持续到连接终止;协议没有在原连接内关闭压缩的命令。
  • 应用层压缩启用后,后续 STARTTLS、AUTHINFO 与 MODE READER 都不再可用;要同时获得 TLS、认证和压缩,只能按这个先后顺序建立。
  • 数据发送时先压缩,再经过 SASL 与 TLS,因此词典范围、可观察长度、刷新与损坏处理都是安全控制,不只是带宽参数。

为了省字节而先关上两扇门

第一个客户端没有发错命令。服务器确实公布了 COMPRESS DEFLATE,请求也得到成功答复。问题出在成功之后:压缩已经成为这条连接共同认可的底层状态,后面的安全协商不再能插入它前面。

若它此时发送 STARTTLS,服务器以 502 拒绝;若尚未认证却发送 AUTHINFO,结果同样如此。想重新选择,只能退出并新建连接。节省流量的决定因此比普通选项更像一道单向闸门。

RFC 8054 明确给出了能够同时满足三种需求的顺序:先 STARTTLS,再 AUTHINFO,最后 COMPRESS。这不是审美偏好,而是避免认证秘密进入可被观察长度的压缩上下文。

206 后的第一个字节属于另一种状态

压缩从成功答复末尾 CRLF 之后立即生效。206 那一行本身仍是普通 NNTP;下一字节已经必须由压缩器产生、由对端解压器解释。边界不能含糊,否则双方连下一条命令在哪里都无法达成一致。

因此 COMPRESS 禁止流水线发送。客户端必须看见答复,才能决定下一条命令采用普通行格式还是压缩流。语法错误、不支持的算法和资源不足分别走失败路径;它们不改变连接。只有成功才跨过边界。

成功同时作用于两个方向。早期的非标准做法只压缩服务器的特定回复;新扩展把所有后续命令和回复都放在同一无损层下。客户端的重复命令、服务器的大型列表、文章头和正文都可能受益,不必为每个命令发明一个压缩版。

一条连接只有一种压缩事实

压缩启用后,服务器不再公布 COMPRESS 与 STARTTLS。客户端也不得再次启用压缩或协商可能带有 TLS 压缩的安全层,MODE READER 也被排除。协议维护的是一个简单但严格的共同事实:下一字节是否属于既有压缩流。

它没有提供 UNCOMPRESS。停止压缩的办法是 QUIT,然后新建连接。这一限制避免一端已恢复明文、另一端仍在等待压缩块的状态分裂。

RFC 3977 允许能力列表随会话状态改变。RFC 8054 还特别指出,不能把上一条连接缓存的压缩能力当成本次连接的现时承诺,因为压缩会影响加密安全。能力发现是一项实时观测,而不是永久配置。

压缩看到加密前的明文规律

发送方向的处理顺序是:先应用层 COMPRESS,再经过已有的 SASL 安全层,最后由 TLS 保护;接收方向按相反顺序处理。只有先压缩,算法才能找到重复模式。但正因为它看见明文,输出长度也可能携带关于模式的信息。

加密隐藏内容,却未必隐藏压缩后的长度。如果攻击者能控制部分输入并观察长度变化,秘密与已知文本的共同片段可能被推断。RFC 8054 把 CRIME 与 BREACH 作为这种风险的例子,并把账户凭据划为最明确、最容易避免的秘密。

所以压缩成功后不得再认证。未认证客户端此时看见的 AUTHINFO 要么消失,要么没有可用参数;有效的认证命令也必须被拒绝。RFC 4643 定义认证动作,但压缩状态决定这条连接是否仍允许进入它。

TLS、认证与压缩没有相互替代

RFC 4642 定义 NNTP 的 TLS 过渡。它保护一段传输链路,却不会自动证明账户身份。AUTHINFO 让服务器接受某个认证主体,却不会加密链路。COMPRESS 改变字节表示,也不授予访问权。

协议只是规定三者组合时的安全顺序,并没有把三种权力合并。先 TLS、再认证、最后压缩,意味着秘密先进入受保护会话,再决定是否承担长度侧信道风险。先压缩则让系统无法安全地把凭据追加进已有词典。

TLS 自身曾能协商压缩,但 RFC 8054 引用的最佳实践要求关闭 TLS 级压缩。应用层 COMPRESS 的优势之一,是能够在了解 NNTP 状态后、尤其在认证完成后才选择是否启用。

词典边界就是信任边界

DEFLATE 通过已经看过的数据寻找重复。RFC 1951 定义了这种无损格式;RFC 8054 把 DEFLATE 规定为扩展必须实现的算法。每一端可以为自己发送的方向选择合理压缩强度,对端解压器负责适应,因此参数并不必然对称。

公开材料和私密文章不应在受保护连接中共享同一压缩历史。不同私密文章也最好不要共用词典;清空 DEFLATE 词典是规范提出的一种缓解手段。是否共享状态,决定了哪些文本模式可能共同影响可见长度。

规范并没有声称“加密加压缩必然泄密”。它要求把风险显式交给策略:安全层存在时,不应在用户不知情的情况下自动启用压缩。

损坏后不能靠寻找下一行恢复

发送端必须把提交给压缩器的所有数据纳入输出,并适当刷新,让对端能够完整解压。它可以针对难以压缩的附件调整级别,但不能把交互式命令无限留在缓冲区中。

如果接收端遇到无效或损坏的压缩数据,就立即关闭连接。它不尝试扫描下一个看似正确的 NNTP 行。解压词典一旦分叉,一个碰巧出现的换行符也不能恢复此前丢失的共同历史。

这与“必须重连才能停止压缩”属于同一设计:重连建立新的、可证明的起点;在旧流中猜测只会制造两个不同的现实。

最后一层承担的是顺序责任

IANA NNTP 参数注册表 记录 COMPRESS 能力与 DEFLATE 算法。注册保证名称和规范引用一致,不证明某台服务器部署了它,也不评价具体配置是否安全。

NNTP 把压缩做成通用双向层,避免了命令变体的蔓延;代价是一次成功请求会永久改变连接的可达状态。206 不只是“以后更省流量”,还意味着某些此前能做的安全动作现在必须拒绝。

真正的历史机制不是 DEFLATE 的压缩率,而是顺序本身:先保护链路,确认身份,再在明确接受风险时压缩。性能层之所以必须最后到场,是因为它一旦开始记住对话,就不再适合让凭据从它后面经过。

来源