Go 1.27.3 / 1.26.10:crypto/x509 拉黑中间证书后的升级与排查
Go 1.27.3 与 1.26.10 于 2026-10-19 发布,这一版只包含一个安全修复:crypto/x509 针对某个特定 CA 的安全问题做缓解,拉黑一小批中间证书。Verify 现在会过滤包含这些中间证书的链,不会把这类链返回给调用方。对应 CVE-2026-111943、Go issue 82345,官方建议立即升级。
修复内容
问题的核心不在根证书,而在中间证书。某些中间证书已经不应再被信任,但部分环境里的信任材料并不会自动失效。修复方式是在 crypto/x509 中加入 denylist:当调用 Verify 且待验证的链中包含已知不可信的中间证书时,直接返回错误。
这意味着失败点被前移到了证书校验层。TLS 握手失败时看到的是 x509 相关错误,而不是证书过期、域名不匹配之类的常见提示,排查方向需要相应调整。
GO-2026-6809 与受影响版本
GO-2026-6809 于 2026-10-21 发布,符号为 Certificate.Verify。受影响区间:
before go1.26.10from go1.27.0-0 before go1.27.3
也就是说,1.26 线需要升到 1.26.10,1.27 线需要升到 1.27.3。该条目别名 CVE-2026-111943,相关记录还有 RUSTSEC-2026-0381、RUSTSEC-2026-0382、RUSTSEC-2026-0383。
系统信任库为什么不一定跟得上
大多数平台上 Go 使用系统根证书,但在一些较窄的场景下,系统信任库不会自动更新。只升级系统 CA 包(如 ca-certificates)不足以在所有环境覆盖:
- 容器镜像可能长期固定某个旧版本 CA 包,构建时不会重新拉取。
- 旧发行版的更新渠道可能滞后,或该中间证书并不在其吊销范围内。
- 离线镜像、内网制品库中的基础镜像同样可能持有旧中间证书。
因此这次缓解放在 Go 自身的 Verify 层,而不是依赖平台信任库的状态。升级 Go 工具链和重新构建二进制,是覆盖面更确定的做法。
Go 1.27 起 SystemCertPool 的行为变化
Go 1.27 release notes 中 crypto/x509 有一处相关变化:SystemCertPool 在 Windows 和 Darwin 上也尊重 SSL_CERT_FILE 和 SSL_CERT_DIR。设置这两个环境变量时,根证书从磁盘加载,并改用 Go 原生证书校验器,而不是平台证书校验 API。
这会影响两类环境:
- 之前只在 Linux 上通过
SSL_CERT_FILE注入自定义根的部署,在 Windows / Darwin 上行为可能改变。 - 依赖平台校验 API 特性(如系统级策略)的场景,在设置环境变量后会走 Go 原生校验路径。
如需关闭该行为,可设置:
GODEBUG=x509sslcertoverrideplatform=0
排查方法
先确认运行时版本,避免出现「代码已改、镜像里还是旧 Go」的情况:
go version
go list -m all | grep '^go '
在容器内对比实际下发的证书链:
openssl s_client -showcerts -connect example.com:443 </dev/null
观察输出中的中间证书是否属于被拉黑的那一批。随后用 Certificate.Verify 打印错误原文,确认失败发生在校验链阶段:
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"log"
)
func main() {
conn, err := tls.Dial("tcp", "example.com:443", &tls.Config{
ServerName: "example.com",
})
if err != nil {
log.Fatal(err)
}
defer conn.Close()
state := conn.ConnectionState()
leaf := state.PeerCertificates[0]
roots, err := x509.SystemCertPool()
if err != nil {
log.Fatal(err)
}
intermediates := x509.NewCertPool()
for _, c := range state.PeerCertificates[1:] {
intermediates.AddCert(c)
}
_, err = leaf.Verify(x509.VerifyOptions{
DNSName: "example.com",
Roots: roots,
Intermediates: intermediates,
})
if err != nil {
fmt.Printf("verify failed: %v\n", err)
return
}
fmt.Println("verify ok")
}
如果业务需要显式信任某条链,应显式配置 RootCAs(以及必要的中间证书),而不是依赖系统池:
pool := x509.NewCertPool()
pool.AppendCertsFromPEM(caPEM)
cfg := &tls.Config{
ServerName: "example.com",
RootCAs: pool,
}
go.mod 固定 toolchain
在 go.mod 中写明版本,避免本地与 CI 使用不同工具链:
module example.com/app
go 1.27.3
toolchain go1.27.3
1.26 线则对应写成 go 1.26.10 与 toolchain go1.26.10。构建镜像时同步更新基础镜像中的 Go 版本,并重新执行 go build,确保产出二进制携带新的 denylist 逻辑。