八张被攻破的证书,一个被打破的假设:eMudhra 事件真正教给我们什么
💡 工具推荐: SSL 证书检查器、哈希生成器、Base64 编解码
2026 年 10 月 19 日大约 17:00 UTC,Cloudflare 得知八张 eMudhra 中间证书关联的私钥被攻破。这些证书被浏览器、操作系统广泛信任,Cloudflare 内部也信任它们。问题的要命之处在于:密钥本身是弱的,所以攻击者可以为任意域名伪造证书,用它伪装成服务器,去拦截或篡改流量。这不是某个应用的 bug,而是一条横跨整个互联网的信任链上游断了。Cloudflare 说安全审查没有发现在其服务上被主动利用的迹象,并已优先为关键服务打补丁。这篇文章讲四件事:他们到底修了什么、你需要升到哪些版本、为什么只升 SDK 不够、以及多数团队缺的那份信任库盘点。
出问题的不是加密算法,而是「我们信谁为某个名字背书」
一、被打破的不是加密,是「信谁」
Cloudflare 特意点明了一句:这项工作解决的是基于证书的身份认证,不是 TLS 加密算法本身的弱点。这个区分很重要,因为它决定了你该改什么。TLS 握手里的对称加密、密钥交换这些算法没问题,坏掉的是「浏览器或系统凭什么相信某张证书代表某个域名」这个判断。当中间 CA 的私钥泄露且密钥够弱时,攻击者可以给任何域名签出一张在你看来完全合法的证书——你连的那台服务器可能根本不是你的服务器。所以修复动作不是换加密套件,而是把那些被攻破的 CA 从信任库里拿掉,并确认每一个实际做验证的地方都拿到了这个更新。
# 1) Find out whether an eMudhra intermediate is in your trust store at all.
# A trust store is just files. Read them instead of assuming.
grep -ril "emudhra" /etc/ssl/certs /etc/pki /usr/local/share/ca-certificates 2>/dev/null
# On macOS, the system store is a keychain, not a bundle
security find-certificate -a -c "eMudhra" /System/Library/Keychains/SystemRootCertificates.keychain
# And your language runtimes ship their own copies:
python3 -c "import ssl; print(ssl.get_default_verify_paths())"
node -p "process.env.NODE_EXTRA_CA_CERTS || 'system default'"二、时间线:一条从发现到全量上线的链路
官方时间线很短,也很说明问题。10 月 19 日约 17:00 UTC,Cloudflare 得知私钥被攻破并开始评估;10 月 20 日 02:00 UTC,Cloudflare One Client 与 WARP 应用在各平台发布安全更新(Cloudflare One Agent 的 iOS 版本仍在等应用商店审核);同日 09:15 UTC,Cloudflare Tunnel(cloudflared)发布安全更新;19:20 UTC,CDN 与服务端 Zero Trust 缓解措施全量部署完成。不到 27 小时,从知情到边缘全量生效。这条时间线的启示不是「他们很快」,而是他们的产品结构让「快」成为可能:边缘是集中控制的,可以一次改完;而客户自建的部分——源站、私有信任库、容器镜像——只能靠客户自己动手。这也正是他们反复强调「部分客户需要对自己运维的软件和信任配置采取动作」的原因。
# 2) The SDK is not the trust policy. Look one layer down.
# Cloudflare's line: an API client may delegate verification to an HTTP
# library, a language runtime, the OS, or a custom trust configuration.
import subprocess, sys
def verify_path(host: str) -> str:
# -show_chain tells you what actually validated, not what you configured
out = subprocess.run(
["openssl", "s_client", "-connect", f"{host}:443",
"-servername", host, "-show_chain", "-verify_return_error"],
input=b"", capture_output=True, text=True, timeout=10,
)
lines = [l for l in out.stderr.splitlines() if "depth=" in l or "verify" in l]
return "\n".join(lines[:12]) or "no chain output"
print(verify_path(sys.argv[1] if len(sys.argv) > 1 else "example.com"))三、你该升到哪些版本
Cloudflare 给了明确的版本下限,别自己猜。Cloudflare One Client(WARP):Android 2.5.6、iOS 1.12.16、Windows / macOS / Linux 2026.8.2115.0。1.1.1.1 应用:Android 6.38.10、iOS 6.31.7、桌面端 2026.8.2115.0。Cloudflare Tunnel 用 cloudflared,升到 2026.10.2;容器部署的,要更新并重新部署修复后的镜像——只 pull 不重新起容器不算完成。如果你用 Cloudflare Zero Trust 加 Gateway,并且客户端是上面这些版本,你已自动受保护。剩下四类要自己检查:客户自配置的 TLS 客户端认证(互信 TLS 的信任库/上传的 CA)、源站 TLS 配置(尤其用 Full (strict) 或 Strict 模式的)、Cloudflare Containers(含 Sandboxes)里的镜像与依赖、以及任何打包自带信任库的应用。
# 3) Test the path your APPLICATION uses, not the one your browser uses.
# System trust store
curl -sS -o /dev/null -w 'system_store=%{http_code}\n' https://internal.example.com/healthz
# The bundle your container image actually carries
curl -sS --cacert /etc/ssl/certs/ca-certificates.crt \
-o /dev/null -w 'image_bundle=%{http_code}\n' https://internal.example.com/healthz
# Mutual TLS with a customer-uploaded CA -- the category most likely missed
curl -sS --cert client.pem --key client.key \
--cacert customer_ca.pem \
-o /dev/null -w 'mtls=%{http_code}\n' https://mtls.example.com/
# A 200 from the browser test proves nothing about a background worker.四、真正容易漏的:SDK 之下那一层
Cloudflare 给开发者的建议只有一句话,但含金量很高:往 SDK 下面看。一个 API 客户端可能把证书校验委托给 HTTP 库、语言运行时、操作系统,或者一份自定义信任配置。所以你升级了 SDK,底层的校验策略可能一点没变——这就是为什么很多人升完版本还在出问题。更危险的是第二句话之后的那个诱惑:升级完你可能会在监控面板里看到一片证书错误,然后有人提议「关掉校验先恢复服务」。千万别。那等于把这次补丁恢复的保护又亲手删掉。正确顺序是:先定位是哪一个信任路径出了问题(系统库?镜像里的 bundle?应用打包的 CA?),修那条路径,然后——这才是关键——把它写成 CI 断言,保证下次构建不会再把它带回来。
# 4) Patch the two clients Cloudflare named, and prove the version.
cloudflared --version # must be >= 2026.10.2
cloudflared tunnel run # redeploy containers with fixed image
# WARP / 1.1.1.1 app floors as published on October 20, 2026:
# WARP Android 2.5.6 | iOS 1.12.16 | Win/macOS/Linux 2026.8.2115.0
# 1.1.1.1 app Android 6.38.10 | iOS 6.31.7 | desktop 2026.8.2115.0
# Cloudflare One Agent on iOS was still pending app review at publication.五、别只用浏览器测试自证安全
Mozilla 的 Dennis Jackson 建了一个众包测试站(emudhra.we-have-a-problem.com),提供浏览器检查、非浏览器测试说明和包更新信息,Cloudflare 也推荐了它。但 Cloudflare 顺手给了个非常关键的免责说明,值得原样记住:测试结果只对被测的那条信任路径有效,不代表设备上每个应用都安全;浏览器测试通过,不能证明后台服务也被保护了;同样,如果应用用的是另一个验证器,curl 测试通过也不能证明什么。这句话翻译成动作就是:你的验证清单要按「应用 × 验证路径」来列,而不是按「机器」来列。一台机器上可能同时有系统库、JVM 自带信任库、Node 的 NODE_EXTRA_CA_CERTS、Python 的 certifi、以及某个 Go 静态编译二进制里的自定义 CA——它们是五个独立的判断题。
# 5) Never "fix" a new certificate error by disabling verification.
# That deletes exactly the protection the patch restored.
# BAD -- silently trusts anything, including a forged eMudhra chain
curl -k https://api.example.com/
requests.get(url, verify=False)
NODE_TLS_REJECT_UNAUTHORIZED=0
# GOOD -- fix the trust store, then assert it in CI
- name: Assert no distrusted CA in image
run: |
if grep -rl "eMudhra" /etc/ssl/certs /usr/local/share/ca-certificates 2>/dev/null; then
echo "distrusted CA still present in image"; exit 1
fi
echo "trust store clean"六、这件事真正的长期教训
Cloudflare 自己写了一段很坦诚的对比:浏览器厂商能「拨个开关」就修好这件事,因为他们要更新的只是一个应用,而不是几百个;他们手里的那个开关叫快速吊销机制,比如 Chrome 的 CRLSets。Cloudflare 在部分产品里也用这类机制。而这个事件的根因,是「信任库被当成一份静态的根 CA 列表」这个延续了几十年的假设——现实是它需要能够被快速更新,需要承载吊销信息、需要承载证书透明度相关的密钥。这也是他们提到 upki 这类项目的原因。落到你自己的系统上,三条可执行的结论:第一,把信任库当成配置而不是常量,它必须能被独立、快速地发布;第二,把「没有弱 CA」写成构建门禁,而不是事后排查;第三,把互信 TLS 和自带信任库这两类「非标准路径」单独建清单,因为它们是这类事件里最容易被漏掉的地方。
信任库是一串决策的集合,就该按决策来管理
📌 常见问题 FAQ
这次事件影响的是 TLS 加密算法吗?
不是。Cloudflare 明确说明这项工作针对的是基于证书的身份认证,而不是 TLS 加密算法的弱点。对称加密和密钥交换本身没有问题,坏掉的是「凭什么相信某张证书代表某个域名」这条信任链的上游。
WARP、1.1.1.1 和 cloudflared 分别要升到哪个版本?
Cloudflare One Client(WARP):Android 2.5.6、iOS 1.12.16、Windows/macOS/Linux 2026.8.2115.0。1.1.1.1 应用:Android 6.38.10、iOS 6.31.7、桌面端 2026.8.2115.0。cloudflared 升到 2026.10.2,容器部署需要更新并重新部署修复后的镜像。
我升级了 SDK,是不是就安全了?
不一定。Cloudflare 提醒说,API 客户端可能把证书校验委托给 HTTP 库、语言运行时、操作系统或自定义信任配置,所以只升级 SDK 可能完全没改变底层的校验策略。你需要确认实际做验证的是哪一层,并逐层确认它已经拿到更新。
看到证书报错,能不能先关掉校验恢复服务?
不能。Cloudflare 明确警告不要靠关闭校验来解决这些错误,因为那会把补丁刚刚恢复的认证保护重新删除。正确做法是定位出问题的具体信任路径(系统库、镜像 bundle、应用自带 CA 等),修那条路径,并把检查写成 CI 断言。
浏览器测试通过就代表我的服务安全吗?
不代表。Cloudflare 引用 Dennis Jackson 的测试站时特别说明:结果只对被测的验证路径有效,浏览器测试通过不能证明后台服务受保护,curl 测试通过也不能证明用其他验证器的应用受保护。要按「应用 × 实际 TLS 路径」逐项验证。