摘要

  • UpgradeCONNECT 只是请求改变 HTTP/1.1 连接的后续解释方式,不是服务器已经选择新协议的证据。
  • 若请求被拒绝,服务器仍可能把客户端抢先送出的内容当作 HTTP/1.1 的后续请求来解析。

低延迟设计常把等待看作浪费。客户端已写完请求,知道自己希望使用什么协议,甚至刚刚在相似连接上成功过;于是它想把新协议的开头提前送出。这个动作可以说明客户端的预期,也可能留下字节层面的观察记录,却不能替服务器作出选择。RFC 9931 把这两件看似相邻的事拆开。

在 HTTP/1.1 中,服务器以 101 Switching Protocols 接受 Upgrade,以成功的 2xx 接受 CONNECT。请求完成是客户端开始升级协议的必要条件,但不是充分条件。服务器可以忽略 Upgrade,要求认证,重定向资源,拒绝不允许或不可达的 CONNECT 目的地。响应未到之前,客户端拥有的是一个已提出的可能性,不是一个已经生效的解析权。

拒绝尤其不能被忽略。RFC 9931 说明,拒绝请求后服务器会继续按 HTTP/1.1 解释同一连接上后来到达的字节。因此,客户端眼中“新协议的第一段数据”可能在服务器眼中成为“另一条 HTTP 请求”。这里的关键不是字节真假,而是哪个解析器在何时有权赋予它意义。

当可信客户端代替不可信第三方传送内容时,问题更尖锐。浏览器可以携带由另一来源控制的路径、头部或内容;代理客户端可以转发本地应用交来的 TCP payload。若 CONNECT 最终失败而客户端提前转发了 payload,代理仍在使用 HTTP/1.1 语法,便可能把那段内容解释为额外请求。RFC 9931 用 request smuggling 与 parser exploit 描述这种有条件的风险。它没有声称某个网络已经遭到攻击,也没有把每一次乐观发送都判作失败。

connect-udp 的修改展示了规范如何收紧边界。客户端只有在 HTTP/2 或更高版本中才可以在获得代理响应前乐观发送 HTTP Datagram UDP 包;HTTP/1.x 明确不得这样做。这个限制不证明某个 HTTP/2 部署正确,也不证明 UDP 已送达。它只拒绝让 HTTP/1.x 中未解决的解析歧义变成性能优化的隐性成本。

对代表不可信 TCP 客户端发送 CONNECT 的代理客户端,RFC 9931 要求二选一:先等待成功的 2xx 再转发 payload,或带上 Connection: close。代理服务器拒绝 CONNECT 后,必须在处理后续请求前关闭底层连接。这些动作维护的是证据顺序:谁提供了 payload、谁接受了转换、谁保留了解析上下文、谁才可能就后续行为作决定。它们不是某个连接已认证、已授权或已完成业务的总证明。

把状态表写清楚,比把仪表盘涂成绿色更重要:客户端提出;请求完成;响应观察到;转换被接受或拒绝;payload 被保留或转发;连接关闭;下游效果被观察。Heng Lu 所说的运行代码优先,并不允许运行痕迹越权。运行痕迹很重要,但它只能证明自己所在层面的事实,不能自动替另一方承担政策、授权或效果判断。

来源