Go 1.27.2 的两个 CONNECT 连接错位问题:反向代理侧怎么排查和处置
Go 1.27.2 和 1.26.9 于 2026-10-08 发布(发布公告,版本历史)。这一版包含两个 net/http 的连接错位(desynchronization)问题,都与 CONNECT 有关:一个在 HTTP/1 客户端 Transport,一个在 HTTP/1 服务端。两者的共同后果是同一个 TCP 连接上两端对字节流的解释变得不一致;如果发生在反向代理上,就会造成跨用户响应投毒或请求走私。
一、客户端:CONNECT 被拒后连接被放回空闲池(CVE-2026-56866)
触发需要同时满足:
- 用
http.Transport发送 HTTP/1 CONNECT,且设置了非空的Request.Body - 服务端用非 2xx 的 keep-alive 响应拒绝该 CONNECT
- 该 Transport 被复用(共享 Transport 的反向代理,包括
httputil.ReverseProxy)
Transport 会把 Body 直接写在请求头之后,不做 framing(既没有 Content-Length,也没有 chunked 编码)。由于规范中的 CONNECT 请求本来就没有 body,服务端读完响应头后就认为这条连接上剩余的字节属于下一个请求;尾随的 body 字节因此被当成管线化的 HTTP/1.1 请求解析。另一边,Transport 收到拒绝响应后把连接放回空闲池,这条连接就进入了错位状态:下一个复用它的调用方会读到那条注入请求的响应。在共享 Transport 的反向代理场景里,这意味一个用户的响应可能被另一个用户拿到。
修复有两处:
- HTTP/1 Transport 在发完 CONNECT 请求后直接关闭连接,不再看响应状态码
ReverseProxy对入站 CONNECT 请求返回405 Method Not Allowed
后者是行为变更,但 ReverseProxy 从来没有把 CONNECT 连接变成双向隧道,因此对现有用法影响不大。问题由 Xclow3n (Rajat Raghav) 报告,编号 CVE-2026-56866,issue golang/go#81740。
二、服务端:返回 2xx CONNECT 之后仍在按 HTTP 读取(CVE-2026-94439)
handler 对 HTTP/1 CONNECT 返回 2xx 后直接 return,没有 hijack 连接,这是会触发问题的写法:
if r.Method == http.MethodConnect {
w.WriteHeader(http.StatusOK)
return // 连接仍被服务器当作 HTTP 连接继续读
}
对 HTTP/1 CONNECT 返回 2xx 会把连接变成隧道,服务器此后不应再把这条连接当成 HTTP。修复前它会继续从连接读取并处理请求。影响主要在请求走私:中间代理认为连接上是隧道数据,服务器认为仍是 HTTP,两边判断不一致。现在服务端在响应 CONNECT 后无论状态码都关闭连接。CVE-2026-94439,issue golang/go#81744。
三、确认是否受影响
先看版本:
go version # 低于 go1.27.2 / go1.26.9 就继续往下查
再看代码里的两个位置:
grep -rn 'NewSingleHostReverseProxy\|httputil.ReverseProxy' --include='*.go' .
grep -rn 'MethodConnect' --include='*.go' .
grep -rn '"CONNECT"' --include='*.go' .
- 用了
ReverseProxy,或自己写了转发 CONNECT 的逻辑:检查 Transport 是不是全局或长生命周期的共享实例。共享并且转发 CONNECT,就落在 CVE-2026-56866 的影响范围内。 - 给 CONNECT 请求设置过
Request.Body:这是客户端侧的直接触发条件。 - 服务端有处理 CONNECT 的 handler:检查 2xx 分支有没有调用
Hijack()。返回 2xx 却没接管连接,就是 CVE-2026-94439 的写法。
四、缓解
- 升级到 1.27.2 或 1.26.9,可同时修掉客户端与服务端两侧。
- 暂时升不了级:不要在共享 Transport 上转发 CONNECT。给 CONNECT 单独建 Transport,或者干脆不转发。
- 代理层直接拒绝 CONNECT。明确不提供隧道能力时,提前拒绝比先拒绝再复用连接安全。
- 服务端如果确实要建隧道,2xx 之前完成 hijack;不打算建隧道就不要对 CONNECT 回 2xx。
五、根因:被打破的「CONNECT 没有 body」
两个问题建立在同一个前提上。客户端认为 CONNECT 没有 body,所以不写 framing;服务端也认为 CONNECT 没有 body,所以把尾随字节当成下一个请求的起始行。一旦请求真的带了 body,或者服务端回了 2xx 却没有把连接交出去,两端对同一串字节的解释就变得不一致——这是连接错位类问题的常见形态。
附:1.27.2 的其他安全修复
- CVE-2026-97032:HTTP/2 server 因 HPACK 编码器竞态崩溃
- CVE-2026-78659:HTTP/2 server 通过 Trailer 头声明大量字段,绕过
Server.MaxHeaderValueCount与Server.MaxHeaderBytes,造成内存耗尽 - CVE-2026-97031:crypto/tls 拒绝畸形的 ECH outer extension 引用
- CVE-2026-94444、CVE-2026-94447:cmd/go 对 golang.org/fips140 与 golang.org/toolchain 的校验和绕过
- CVE-2026-94448、CVE-2026-97030:html/template 在 JS 模板字面量与 yield 关键字上的上下文跟踪
- CVE-2026-94440:net/textproto、mime/multipart 在分片起始剩余限额小于 400 字节时绕过内存限制
- CVE-2026-78669:HTTP/2 反复修改 SETTINGS_INITIAL_WINDOW_SIZE 导致 CPU 占用过高
- CVE-2026-78660:HTTP/2 transport 接受畸形的 framing 相关头,转发给 HTTP/1 客户端时可导致响应走私