摘要

  • draft-ietf-tls-tlsflags-18 用一个 TLS 扩展容纳最多 2040 个无内容功能指示。它仍是处于 I-D Exists 的拟议标准轨工作组草案,不是 RFC 或部署报告;第 18 版只是维护性更新。
  • 同一位可能表示支持或使用意图,也可能是提议、确认或允许的主动通告。每个标志位自己的定义文档决定消息位置、是否需要确认以及如何处理 0-RTT。
  • Daniel Kade 建议附加“标志位解释封套”,保存消息、角色、方向、规范版本、握手证据、恢复语境、实际结果和本地决定。这是编辑性治理建议,不是 IETF 要求。

一次资产盘点把“观察到标志位”和“服务器已启用功能”放进了同一列。统计结果很整齐,直到团队发现增长主要来自 NewSessionTicket,而不是客户端实际尝试功能的连接。系统准确地数了通告,却把通告命名成启用。

问题不是位图不可靠。问题恰恰来自位图太容易存储:数字、布尔值和时间戳都能进入固定字段,携带语义的消息位置、方向和定义文档却被当成附注丢掉。

新版日期没有改变成熟度

Datatracker 记录显示,第 18 版发布于 2026 年 9 月 10 日,是 TLS 工作组的活跃 Internet-Draft,预期走 Proposed Standard 路线。IESG 状态仍为 I-D Exists,工作组状态为 Waiting for Implementation,没有负责的 AD,也没有 telechat 日期。

文档历史和第 17→18 版官方差异给出了重要边界:变化只有版本、日期和到期信息,没有改动操作规则。新日期把草案延续到 2027 年 3 月,并不构成批准、互操作测试或采用证据。

TLS 工作组要解决的成本是真实的。若一个可选功能只需用扩展“存在”来传递信息,每个独立扩展仍要付出四字节开销。把多个无内容扩展装进一串位,可以摊薄固定成本。它压缩了语法,没有创造统一的功能状态机。

最短编码只保证位置一致

第 18 版正文规定标志串为 1 至 255 字节,可表示第 0 至 2039 位。每个字节从最低有效位开始排列,编码必须在最高置位所在字节结束。全零值或尾随零字节属于无效扩展,接收端应以致命 illegal_parameter 终止。

这些规则能让双方找到同一位,也能拒绝不规范的表达。但草案对 flag-type feature 的定义保留了一个不能省略的“或”:存在可能表示支持,也可能表示使用意图。

支持是软件能力;意图是一次交互中的选择。提议是消息事件;确认是另一个事件,而且并非每个标志都需要。真正执行功能、取得预期结果、通过应用策略,又分别需要后续证据。

把它们统称为 enabled=true,不是压缩,而是改变事实。

消息位置决定动作是什么

草案允许未被请求的标志出现在 ClientHello、CertificateRequest 和 NewSessionTicket。若标志出现在 ServerHello、EncryptedExtensions、Certificate 或 HelloRetryRequest,则必须有适用的先前提议;无来源的回应位置标志会导致致命错误。

因此,同一个数字在不同消息里使用不同动词。

ClientHello 中的位由客户端提议。ServerHello 或 EncryptedExtensions 中的对应位可能是确认。NewSessionTicket 中由服务器主动发出的位没有客户端回执。CertificateRequest 与客户端 Certificate 之间,提议与回应的角色又反转。

一个布尔字段容不下这些关系。即使拆成 offered/accepted,也会误读那些本来就不要求回应的标志。工作组 issue 19正是为此明确:确认要求应由各标志的定义文档决定,而不是由公共容器统一强制。

所以“没有确认”可能是正确行为,而不是拒绝或失败。

相同位的确认仍不等于执行

对于确实要求确认的标志,合法回应只能是在 tls_flags 中设置同一位。如果回应需要携带内容,就不应使用这个容器,而应采用有结构的扩展。

这条限制保护了一位信号的边界。提议与确认相配,最多说明双方完成了定义文档规定的信号动作。它没有证明功能随后运行、应用结果成功、本地安全策略批准,或者身份被授予权限。

RFC 8446中的 post_handshake_auth 是现成例子。客户端用空扩展表达愿意接受握手后认证,但这不证明服务器后来发送 CertificateRequest,更不证明客户端提交证书或应用授予访问权。愿意、请求、完成、授权是四个不同状态。

0-RTT 不能共享一个默认含义

TLS 1.3 恢复连接时,0-RTT 数据可能在新握手完成前已经发送。tls_flags 可以出现在相应握手消息中,但草案没有为所有未来标志定义统一的早期数据规则。每个标志文档必须说明自己的 0-RTT 交互。

工作组 issue 16明确记录了分工:公共扩展是编码框架,具体功能仍负责自己的早期数据语义。有的状态可以从 ticket 继承,有的需要新确认,有的通告只影响下一次恢复。

若数据仓库只留下 true,却丢掉完整握手或恢复握手、0-RTT 的提议与接受、使用的 ticket 和决定时点,它就可能把后来取得的状态倒投到更早的数据上。对安全或身份判断而言,这会让尚未存在的授权覆盖已经发生的动作。

“没看到”必须带观察点

草案提醒:ServerHello 和 HelloRetryRequest 中的确认能被被动观察者看到;没有特殊理由时,多数确认应放在加密消息中。

于是路径探针与端点日志具有不同视野。探针可以读明文 ServerHello,却不能据此断言 EncryptedExtensions 中没有某位。端点知道解密后的 transcript,但它代表更高权限的证据来源。TLS 终止代理看到的是自己参与的链路,不自动代表后续连接。

任何“标志缺失”都应先说明从哪里观察、哪些消息可见、采集何时开始、解析器认识哪一版注册表。传感器的无知不能被重写为对端的沉默。

注册表是坐标,不是运行证明

第 18 版请求建立 TLS Flags 注册表,字段包括 Value、Flag Name、Message、Recommended 和 Reference。0–15 采用 Standards Action,16–2039 采用 RFC 8126的 Specification Required。初始拟议条目为第 8 位 resumption_across_names,出现在 NewSessionTicket,Recommended 为 N。

在本次研究截止时,IANA TLS ExtensionType 注册表另行列出公共容器 tls_flags:扩展值 62,可用于 CH、SH、HRR、EE、CR、CT、NST,Recommended 为 N,引用第 14 版草案。这证明一个协调中的注册表状态,不证明某个产品实现、启用或正确执行了任何具体位。

RFC 8447特别说明,Recommended N 不一定表示有缺陷;它可能适用范围有限、用途特定或尚未获得所需共识。指定专家同意登记也不是背书。issue 32促使草案对齐 Y/N/D,并避免让定义功能的第一份文档看似永久掌握 Recommended 状态。

当前的 TLS 1.3 bis与TLS 注册表 bis也仍在工作中。注册表帮助解析器找到规则,不是一次握手的执行轨迹。

致命错误只证明有限范围

接收端因尾随零、全零值或无先行提议的回应而终止,可以形成清楚的协议事实:哪条消息、哪些字节、哪条规则、什么 alert。

它不会自动诊断组织原因。过期库、旧注册表快照、序列化缺陷、错误实验策略和恶意输入都可能得到相似结果。“功能故障”混合了观察与推断。可复核的记录应先保留发送角色、方向、消息、原始值或哈希、解析器版本和准确错误,再另列原因、责任人与修复决定。

给每个位附上解释封套

Daniel Kade 建议建立一份标志位解释封套。

它保存不泄露流量秘密的 transcript 证据引用、TLS 版本、完整或恢复握手、0-RTT 提议/接受/拒绝、发送方角色、方向、准确消息、容器扩展值、原始位串哈希、规范长度、位号、注册表快照,以及定义该位的文档与修订版。

随后标明事件属于提议、确认或允许的主动通告;本位是否要求确认;它对应哪条先前提议;解析验证结果如何。被动可见与端点可见必须分开,并保存解析器及策略版本。

最后记录实际后果:已理解、已选择、已执行、失败、被替代或未知;再附上允许依赖它的决定、责任人、使用者、保留期、不确定性、失效与关闭证据。

这不是 IETF 草案要求,也不改变协议或分配号码。它只是防止下游把一条准确的消息事实扩写成无人证明过的功能状态。

逐级提高主张

“在服务器 NewSessionTicket 中看到第 8 位”是观察。“服务器按 R 版规范通告跨名称恢复”是解释。“客户端随后尝试恢复”是执行事件。“服务器按策略 P 接受”是决定。“应用授权身份”还需要自己的证据。

公共容器节省的是重复字节,治理不应顺手删掉这些动词。TLS 标志位可以准确证明消息里有一位;单凭它,不能成为功能状态记录。

来源