一个没人盯的握手消息:Go crypto/tls 拒绝服务漏洞分析
先说结论:这不是一个需要特殊配置才能触发的 bug,默认情况下,凡是使用受影响 Go 版本编译的 TLS 服务端,都可能被一个格式合法、动作平凡的 TLS 1.3 握手消息打到 CPU 满载。
入口:KeyUpdate
TLS 1.3 引入了 KeyUpdate 消息,设计目的是让通信双方在不重新握手的情况下更新会话密钥。它是标准流程的一部分,本身没有任何"恶意"的外观。
在 Go 标准库 crypto/tls 的实现中,KeyUpdate 消息无论当前握手是否完成,都被视为"推进 TLS 状态机"的消息。问题就在这里:一个本应在握手完成后才出现、且应受次数限制的消息,在握手尚未完成时也被无条件处理。
恶意客户端可以建立一个 TCP 连接,发送 ClientHello 后,不断发送 KeyUpdate 消息。服务端对每条消息都执行一次完整的 TLS 1.3 密钥派生(HKDF key derivation)运算。该运算是 CPU 密集型的,且实现中没有对 KeyUpdate 消息的接收次数或状态上下文做限制。结果是,攻击者用极小的带宽代价,换取服务端无上限的 CPU 消耗,形成典型的拒绝服务。
为什么默认就中招
因为这不是旁路逻辑,而是主握手路径上的问题:
- 现代 Go 应用默认协商 TLS 1.3,无需任何额外配置;
KeyUpdate是标准 TLS 1.3 会话流程的一部分,无法通过协议配置绕过;- 触发不需要认证、不需要应用层数据、不需要完成握手——只要连接建立,攻击就可以开始。
这意味着,你不需要跑在什么奇怪的边缘场景下。一个普通的 net/http 服务端,只要用了 TLS,就在暴露面上。
影响面从根因来看,取决于 crypto/tls 内部对 KeyUpdate 的状态校验逻辑。在该逻辑被修正前,所有使用受影响 Go 版本编译的服务端二进制都受影响,与部署方式(容器、裸金属、云 LB 后)无关。
影响版本
官方修复版本对照如下:
| 版本线 | 修复版本 |
|---|---|
| Go 1.25.x | go1.25.13 |
| Go 1.26.x | go1.26.6 |
| Go 1.27.x | go1.27.0-rc.3 |
受影响范围:
- go1.25.13 之前的所有 Go 1.25.x;
- go1.26.0-0 至 go1.26.6 之前;
- go1.27.0-0 至 go1.27.0-rc.3 之前。
漏洞编号:GO-2026-6090 / CVE-2026-56862
CVSS 评分:7.5 HIGH
CWE 分类:CWE-770(无限制资源分配)
自查
检查你构建服务端二进制时用的 Go 工具链版本:
go version
如果输出落在上面的受影响区间内,那么该二进制内置的 crypto/tls 就存在此漏洞。这里要看的是编译时的 Go 版本,不是运行时容器的 base image 版本。
如果你在 CI 里统一管理 Go 版本,检查 CI 配置中的 go.mod 指令或工具链指定版本即可。
修复
升级 Go 工具链到以下任一版本及以上:
go1.25.13go1.26.6go1.27.0-rc.3
然后重新编译所有 Go 服务端二进制并重新发布。
没有官方 backport 机制。Go 只维护当前两条主要版本线,go1.25.13 和 go1.26.6 分别对应两条线的修复。如果你用的是更老的 Go 版本,需要先升级到一条受支持的版本线,再升到对应修复版本。
运维层面:除了升级还能做什么
原文未提供官方临时缓解方案。 以下是从运维角度的分析判断,不作为官方规避手段。
网络层限速。在 LB/防火墙层面对单 IP 的新建连接速率做限制,可以降低攻击面,但无法根治:攻击者只要建立少量连接并持续发送
KeyUpdate,每个连接都会消耗服务端 CPU,限速只能延缓,不能阻止。限制单连接时长/流量。在四层 LB 上对空闲连接或异常小流量长连接做超时断开,可以打断攻击者维持连接的行为。但对攻击者而言,重新建连成本很低。
不要关闭 TLS 1.3 作为临时方案。禁用 TLS 1.3 会强制降级到 TLS 1.2,改变整个加密配置基线,引入协议降级风险,并且并不保证后续 TLS 1.2 实现中没有类似问题。这不是一个可接受的缓解措施。
监控告警。关注服务端 CPU 突增和单连接高频握手消息的日志特征,有助于提前发现问题,但属于事后响应。
从实际效果看,升级是唯一彻底的修复手段。临时措施只能作为升级窗口期的缓冲。
小结
这个漏洞的本质是一个没有做好状态校验的握手消息处理路径:合法协议消息、无身份要求、无次数限制、CPU 密集操作、默认协议开启。不需要复杂的攻击链,几个 TCP 连接就能打满 CPU。
排查很简单,看 Go 版本;修复也很直接,升级重编译。不要在这个问题上做临时绕过的架构决策。