摘要

  • ALPN 把应用协议选择放进 TLS 握手,省去另一次往返;客户端提交有顺序的候选,服务器只能从共同集合里选出一个。
  • 没有共同协议时,正确结果是致命的 no_application_protocol,而不是悄悄猜一种默认语法。选中的标识只约束本连接。
  • TLS 会话票据可以帮助恢复密码学状态,却不能携带上一条连接的 ALPN 决定。TLS 1.3 保护了服务器回答,也没有让客户端候选、应用授权或部署事实自动获得保密与证明。

一张抓包为什么少了服务器的答案

运维人员比较两次连接。客户端发出的 ALPN 候选相同,服务端最终都使用 HTTP/2。较旧的 TLS 1.2 抓包能够直接看到服务端在 ServerHello 中返回的选择;TLS 1.3 抓包里,同一位置却没有那个值。

如果把“抓包中不可见”直接写成“服务器没有协商”,就会把协议演进误判成生产故障。相反,如果把“TLS 1.3 已加密服务器选择”扩大成“ALPN 已完全不可见”,又会遗漏普通 ClientHello 仍携带客户端候选的事实。

RFC 7301 最初有意识地把服务端选择放在当时的 ServerHello 里。共享端口以后,网络设备不能只靠 TCP 或 UDP 端口判断上层协议;一个可见标记便于按连接区分服务。规范同时提醒:客户端发送的协议名可能泄露敏感信息,设计新标识时不能把可见性当作无害条件。

TLS 1.3 的 RFC 8446 改变了服务端这一侧。ALPN 回答进入 EncryptedExtensions;这是服务端第一个使用握手流量密钥加密的消息。客户端候选仍位于 ClientHello。可见边界因版本与方向而异,而不是从“全明文”一步跳成“全秘密”。

443 已经不能说明后面由谁解析

传统端口号曾经是应用协议的强提示。TLS 让多种应用共享保护层和入口,尤其集中到 443。共享降低部署门槛,却让“连到这个端口就一定说这种语法”的推断失效。

TLS 完成以后再发起一次应用协商,可以消除歧义,但多花一轮网络往返。直接发送一种语法,再让服务器尝试识别,则会把分歧拖到应用数据之后。届时同一串受完整性保护的字节,仍可能被两端当作不同协议。

ALPN 借用了本来就要发生的握手。客户端在 ClientHello 中提供集合,服务端在握手完成前给出单一选择。第一段应用数据抵达时,连接已经拥有共同语法。

RFC 8170 把这段历史放在 HTTP/2 部署中说明:受 TLS 保护的 HTTP/2 需要避免额外往返,因此采用 ALPN;非加密变体使用 Upgrade。后来 ALPN 成为协商未来 HTTP 版本的主要方式。这里的关键不是把 HTTP 放进 TLS,而是把“端口暗示”改成“每条连接的显式决定”。

客户端排序没有夺走服务器选择权

ALPN 候选是非空、不截断的不透明字节串。客户端按自己的偏好排序。排序说明它更希望使用什么,不是向服务器下达第一项必须获胜的命令。

服务器有自己的支持集合和优先级。RFC 7301 要求它只在客户端列出的项目中选择,并建议从交集中选服务器最偏好的一项。客户端因此掌握可接受边界,服务器保留本地服务政策;谁也不能替对方增加一种语法。

回答只能是一个标识。一旦返回,该标识就对当前连接具有决定性。服务器不能宣布 h2,随后因为后端方便而发送 HTTP/1.1。ALPN 不是能力清单的回显,而是对后续解释方式的承诺。

这也说明监控为什么必须保存双方证据。只记“客户端支持 h2”,无法解释服务端为什么选了 http/1.1;只记软件版本“支持 h2”,无法证明这台实例、这个租户或这份配置当时允许选择。真正可审计的是候选顺序、服务端策略版本、实际选择与实例身份。

没有交集就不该假装兼容

如果服务端不支持客户端列出的任何项目,RFC 7301 要求返回致命的 no_application_protocol。这不是普通应用层拒绝,因为应用语法尚未确立;也不是让客户端随便选一种旧协议继续发送。

静默回退看起来提高成功率,实则抹掉两端唯一共同知道的事实:它们没有共同语法。客户端不知道服务端选择了什么,服务端可能等待客户端从未承诺的格式。TLS 只保证字节没有被篡改,并不会让两个解析器自动达成一致。

RFC 9325 后来把严格执行这一规则写进 TLS 最佳实践。原因不只是互操作性,还包括跨协议混淆:本来面向一种服务的消息若被另一种服务解析,可能产生意外操作。ALPN 不能消除所有此类攻击,但服务端若接受客户端未提出的标识,就主动丢弃了已有的一道边界。

失败因此是一种有价值的生产证据。它能暴露仍需旧协议的客户端、尚未完成的节点发布、错误的优先级或不兼容的 QUIC 版本。为了让错误率好看而隐藏它,会把可修复的不兼容变成无法定位的语义漂移。

票据能恢复计算,不能恢复语法

TLS 会话恢复与 session ticket 的目的,是在政策允许时复用一部分密码学建立成果。如果上一条连接选过 h2,最省事的想象是让票据把它一并带回来。

RFC 7301 明确否定这种继承:ALPN 是连接属性,不是会话属性;恢复时旧扩展内容无关,只有新握手里的值有效。

原因并不神秘。客户端可能已经删除一种协议。服务端可能把新协议提升到首选。票据可能落到另一台节点。负载均衡、区域或租户配置可能改变。如果旧选择绑定在票据上,性能优化就会取得锁定未来应用政策的权力。

同一张票据前后选择不同,也不自动说明恢复损坏。它可能准确显示新节点的交集与偏好。诊断必须比较两次握手各自的候选、选择、配置与路径,不能用“session resumed”概括成“整个应用会话原样继续”。

0-RTT 不能抢在新语法前面

TLS 1.3 允许符合条件的恢复连接携带 early data。客户端可在新握手完全结束之前先发应用字节。这使“旧选择不继承”成为立即影响数据处理的规则。

RFC 8446 规定:除非新连接选中了相同的 ALPN 协议,TLS 实现不得自动重发 early data。若旧连接是 HTTP/2,新连接却选了 HTTP/1.1,把原字节机械地塞进去,会在协议意义改变后重复动作。

“ALPN 相同”也只是一项必要条件。它不能证明请求可重复、凭据仍有效、前一次没有执行,或应用具备幂等性。重放判断属于应用。TLS 能确认的是更窄的事实:在协议名已变化时,自动重发连语法连续性都无法成立。

票据因此不能成为业务去重凭证。它既不承诺 exactly-once,也不授权某个 origin。它只在密码学协议定义的范围内参与新握手。

HTTP/2 在选择以后仍要发送前言

RFC 9113 规定 TLS 上的 HTTP/2 使用 h2,并必须通过 ALPN 选择。h2c 描述非 TLS 版本,不能在 TLS ALPN 中提交或选中。

TLS 完成后,双方还必须发送 HTTP/2 connection preface。客户端以特定字节序列和 SETTINGS 开始,服务端以 SETTINGS 开始。前言不是对 ALPN 的无谓重复。

ALPN 回答“哪一种语法被允许接管这条连接”;前言回答“接下来实际收到的字节是否按这种语法开始,并带有哪些初始参数”。服务端选了 h2 却收到错误前言,说明故障发生在选择之后。只看到前言却没有握手记录,也无法证明 TLS 两端曾经同意 h2。

这两层证据还能揭示错误路由:边缘 TLS 终结器可能正确选择 h2,却把流量交给只会 HTTP/1.1 的后端。把全部数据压成一个“HTTP/2 已开启”配置项,会隐藏真正跨越组件的交接失败。

QUIC 把协议选择纳入连接成立条件

RFC 9001 用 TLS 保护 QUIC,并要求密码学握手提供经过认证的应用协议协商;没有其他机制时必须使用 ALPN。若没有谈成应用协议,端点必须立即以对应错误关闭连接。

此外,某个应用协议可以限制自己适用的 QUIC 版本。服务端不能只看名称有交集,还必须确保选择与客户端采用的 QUIC 版本兼容;客户端也要拒绝不兼容组合。

这里同时出现三个本地决定:客户端能说什么,服务端愿意提供什么,本条传输连接能承载什么。标准没有设立中央调度者替双方分配协议。它提供一个共同的选择与失败语法,让自治端点在应用数据产生状态之前达成一致。

注册表只证明字节名有归属

IANA TLS 扩展与 ALPN 标识注册表 记录扩展类型 16,并以 Expert Review 管理协议标识。表中包括 http/1.1、h2、h3,也保留了明确禁止在 TLS ALPN 使用的 h2c。

注册解决的是独立实现对同一协议使用同一字节串的问题。它不证明客户端实际发送,不证明服务端实际选择,不统计流量,也不担保选择后的程序实现安全。

生产证据必须分层:注册表说明一个值被分配给什么规范;握手说明这条连接实际提交和选择了什么;应用前言与请求轨迹说明后续字节是否进入正确解析器;授权系统再说明某项操作是否允许。

ALPN 的价值正来自这种克制。它给连接一种明确语法,却不把语法选择扩张成身份、授权或永久会话属性。旧票据可以回来,旧协议决定必须留在旧连接里。

同一个证书也不能替新连接回答

票据之外,还有两个常被误当成连续性证明的条件:地址相同与证书相同。它们都很重要,却都没有覆盖 ALPN 的问题。地址说明网络路径抵达某个 endpoint;证书验证说明 peer 在客户端信任规则下给出了可接受的身份材料。两者都没有说明这一次该由哪个应用 parser 接管数据。

一套共享证书可以部署在多个节点上。节点的 ALPN 支持、优先级和后端映射可能分批更新。证书仍然有效时,应用选择却可以不同。反过来,同一 ALPN 值也不说明证书检查一定成功,更不说明 server 对某个 HTTP origin 具有回答权。

因此故障记录应避免一个“connection secure”总字段吞掉全部阶段。至少要分别保留:网络建立、证书验证、ALPN 交集、服务端选择、应用前言、origin routing 和 authorization。若一条恢复连接从 h2 变成 http/1.1,调查应先对比当前节点和政策;若证书失败,则 ALPN 选择无论是什么都不能补救身份判断;若应用返回 421,则说明协议连接已经存在,但 origin 与连接上下文仍不匹配。

把这些决定拆开不会增加中心协调者,反而允许各责任方只对自己的事实负责。TLS 团队可以轮换证书和 ticket key,应用团队可以改变 protocol preference,origin 团队可以拒绝错误连接上下文。连接成功不是一张覆盖所有边界的通行证。

来源

这些资料提供规范、建议与注册状态,不是当前部署率、流量占比或具体产品配置的普查。