e穆德拉(eMudhra)的证书颁发机构(CA)私钥之一只是 4,还有 56、115;我从公开证书恢复全部私钥不到一秒。10 月 17 日,安全研究员向苹果(Apple)、谷歌(Google)、微软(Microsoft)、谋智(Mozilla)和 e穆德拉报告八张中级证书私钥泄露;当晚谷歌浏览器(Chrome)经 CRLSet 屏蔽,谋智在火狐(Firefox)经 OneCRL 屏蔽。10 月 20 日,谋智发 NSS 更新移除 e穆德拉根证书,谷歌浏览器确认其进入 Chrome 根存储(Chrome Root Store) v43。八张含六 TLS、两代码签名 CA,2025 年 4 月建,5 月由塞克蒂戈(Sectigo)AAA 证书服务(AAA Certificate Services)根交叉签名。
正文
eMudhra 某个 CA 的私钥就是数字 4。不是以 4 开头的一串数字,也不是 4 后面跟着几百个随机数字,而就是 4。另一个是 56,还有一个是 115。我知道这些,是因为我刚刚在笔记本电脑上从它们的公开证书里把全部私钥都恢复了出来,用时不到一秒。
发生了什么
10 月 17 日星期六,一名安全研究人员向 Apple、Google、Microsoft、Mozilla 和 eMudhra 报告,eMudhra 八张中间证书的私钥已泄露。Chrome 当晚推送 CRLSet 更新将其屏蔽,Mozilla 几小时后通过 OneCRL 在 Firefox 中屏蔽它们。10 月 20 日星期二,Mozilla 公开宣布,并发布 NSS 更新移除 eMudhra 根证书;Chrome 也确认将该根证书移入 Chrome Root Store v43。
八张中间证书包括六个 TLS 签发 CA(DV、OV、EV,各两代)和两个代码签名 CA,全部创建于 2025 年 4 月。它们每一个还在 2025 年 5 月被 Sectigo 的 AAA Certificate Services 根证书交叉签名。
这是“发生了什么”,但真正有意思的是“怎么发生的”。Ian Carroll 在讨论串里用一句话概括:
它们本质上就是任何人都能分解的弱单字节密钥,而且这个问题无法被遏制。
私钥到底是什么
这些是 P-384 曲线上的椭圆曲线密钥。私钥只是一个数字 d。公钥(也就是放在证书里给所有人看的那个)是 d × G,其中 G 是曲线上一个所有人都使用的固定点,“×”表示用曲线的特殊算术把 G 自加 d 次。
正向计算很容易,但反过来,从公钥推回 d 被认为是不现实的。这就是整个安全模型,而且只有在 d 极大时才成立。P-384 私钥应当是一个随机的 384 位数字,约有 2³⁸⁴ 种可能(大致相当于 10¹¹⁵),所以没人会去穷举。
eMudhra 的密钥是这样的:d = 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004;而正常的 P-384 私钥应类似:d = 0x48ac06790886c80c48d31691fd4c0fe9ce3207f9807f2fe571d6e4ec426d78644298fc53df2b92566970e757ac998c34。也就是 47 字节的零,然后一个字节,八张密钥全都是这样。一个字节只有 256 种可能值,所以“搜索”就是计算 1 × G、2 × G、3 × G……直到与证书匹配为止,最多 255 步。
于是,整套体系——这些 CA 可能签发的每一张证书、每一个信任它们的客户端——都建立在 8 个比特之上。
我验证了
Mozilla 在公告里附上了受影响的证书,于是我把全部 16 张证书都拉下来(8 个密钥,每个用于两张已签发证书:一次由 eMudhra 签发,一次由 Sectigo 交叉签名),并对它们进行暴力破解。每一张都瞬间被攻破,六个 TLS 密钥和两个代码签名密钥同样脆弱:
| 中间证书 | 类型 | d | | --- | --- | --- | | EM DV TLS CA - G3B | TLS | 156 | | EM DV TLS CA - G3C | TLS | 230 | | EM EV TLS CA - G3B | TLS | 115 | | EM EV TLS CA - G3C | TLS | 56 | | EM OV TLS CA - G3B | TLS | 155 | | EM OV TLS CA - G3C | TLS | 4 | | EM OV CS CA - G3B | 代码签名 | 66 | | EM OV CS CA - G3C | 代码签名 | 237 |
有意思的是,这些证书看起来没有任何问题。由 d = 4 推导出的公钥是曲线上一个完全有效的点,编码没问题,曲线也没问题,所有结构检查都能通过。唯一能发现问题的办法就是去试。
为什么“它无法被遏制”
通常 CA 密钥泄露,是有人把它偷走了!有窃贼,有事件发生的时刻,你至少还能推断现在谁掌握了该密钥。
但在这里,没人需要偷任何东西。从证书存在的那一天起,这把密钥实际上就是公开的。自 2025 年 4 月以来,任何人在任何时刻看过其中一张证书,都能在普通硬件上几毫秒内恢复出密钥。这是一个长达 17 个月的窗口,而且无法知道是否有人已经这么做了。
Chrome 指出,由于 Chrome 强制实施 Certificate Transparency,而且从未有链接到这些 CA 的 TLS 证书出现在 CT 中,因此默认情况下 Chrome 不会信任任何伪造证书。这对浏览器来说是真的,也令人安心。但 CT 只能检测错误签发,不能阻止它,而且很多软件根本不检查 CT。
今天谁处于风险之中
主流且及时更新的浏览器基本没问题。Chrome 和 Firefox 直接屏蔽这些中间证书,并且都要求 CT,Safari 也要求 CT。
但其他一切就是另一回事了。大多数非浏览器 TLS 客户端(基本上就是其他所有东西,在默认设置下),比如 curl、Python、Node、Go、Java、git、包管理器以及你用的每一个脚本和守护进程,在这里做的三件事很关键:它们信任磁盘上的 CA 捆绑包,默认不检查吊销,也不检查 CT。因此,eMudhra 和 Sectigo 已经吊销这些中间证书对它们毫无帮助。我在谈论 TLS 和 PKI 时经常发现,客户端生态到底有多庞大容易被忽视。我们在培训中关注现代吊销方案(如 CRLite)时也会聚焦这点:CRLite 在桌面浏览器上效果很好,但对任何其他 TLS 客户端都没用。想想现在有哪些东西需要更新根存储,而这只是冰山一角:
- curl | sh 安装程序和自更新程序。 许多工具通过 HTTPS 下载脚本或二进制文件来安装或更新,且不做签名检查(rustup、nvm、Homebrew 安装程序、无数“install.sh”页面、IoT 固件更新程序)。这里的伪造证书意味着运行攻击者的代码,而且往往是以 root 身份运行。
- CI/CD 流水线。 GitHub/GitLab runner、Jenkins 和构建容器在获取依赖、推送构件的同时,还持有云密钥、仓库令牌和签名密钥。它们运行在共享网络中陈旧的基镜像上,底层全是 curl、Python、Go 和 Node。
- 服务器到服务器的 API 调用。 支付服务商、webhook、OAuth token 交换和云 SDK(AWS CLI 和 boto 使用自己的 certifi 包;Terraform 和 kubectl 使用 Go)。这些请求在头部携带 API 密钥和客户数据。
- IoT、嵌入式和 OT 设备。 摄像头、路由器、汽车、医疗设备、智能家电、支付终端和工业控制器回连获取命令和更新。它们的证书捆绑包被冻结到 2039 年,这就把“无法被遏制”变成了现实。
- Windows 代码签名。 这不是 TLS,但缺陷相同:Windows 直接信任 eMudhra CS 根证书。用 EM OV CS CA - G3B/G3C 签名的恶意软件,在任何不检查吊销的东西看来,都像是来自真实发布者的已签名软件。
我在自己的机器上用本地服务器和一张 api.stripe.com 的证书做了测试,这个名字我们都该同意价值很高。在默认设置下,curl 显示“SSL certificate verify ok”,Python 完成握手,Node 显示“authorized: true”。我测试的每台 Linux 和 macOS 机器都是如此。在 Windows 上,CryptoAPI 会接受该链,除非开启了吊销检查;即便开启,也只有两条路径中的一条返回已吊销。相关输出显示颁发者为 C=NL; O=eMudhra BV; CN=EM DV TLS CA - G3B,最后返回“SSL certificate verify ok”。
你可以在该证书里放入任何其他主机名,这样就具备了拦截该设备流量所需的一切,只要进入网络路径即可。
你现在应该做什么
幸运的是,对可以更新的设备来说,必要的步骤并不复杂,建议归根结底就是“更新它们”。问题在于那些无法更新或不易更新的设备。
- 更新所有附带 CA 捆绑包的东西:操作系统的 ca-certificates 包、certifi、Node、Homebrew,并在基镜像纳入变更后重建容器镜像。
- 无法更新的设备才是难点。路由器、打印机、电视、固件被冻结的家电会一直信任这些证书,直到 2039 年 7 月到期……
八个比特。在整整 17 个月里,这就是大量非浏览器软件与任何站点的伪造证书之间的全部屏障;一旦伪造证书出现,你所有加密流量的安全都会被击穿。
来源
MDSP 讨论帖:https://groups.google.com/a/mozilla.org/g/dev-security-policy/c/0R3XLJL6FuE/m/Kp1v6CPuAAAJ?utm_source=scotthelme.co.uk
研究公告:https://emudhra.we-have-a-problem.com/?utm_source=scotthelme.co.uk
2026 年 10 月 20 日 20:16 UTC 更新:发布后不久,Mozilla 公开了他们的事件工单:https://bugzilla.mozilla.org/show_bug.cgi?id=2083449&ref=scotthelme.co.uk
eMudhra 在该工单中给出了他们的说法:似乎是仪式中使用的脚本存在 bug,在 HSM 上生成了第二对弱密钥,由于名称相似,人工错误导致 CSR 使用了错误的密钥。以上正文内容不因这些新信息而改变。我还确认,用一条简单的提示词通过 AI 代理也能恢复该 CA 的私钥;Claude 仅用一条提示就恢复了私钥。
评论
讨论气氛以震惊和讽刺为主,多数人把 eMudhra 中间 CA 的私钥弱到近乎 8 位熵视为极端疏忽,甚至像手工挑选或劣质随机源产物。少数人怀疑背后是内部泄露或故意行为,而另一条分歧主轴是:这究竟是单一密钥生成 bug,还是上百个受信任 CA 的信任体系本身过度膨胀、审核失灵。
认为私钥弱到像手工挑选或劣质随机源产物
- 评论集中震惊于私钥弱得离谱:熵只有约 8 位,像是全零只留一个非零字节,或所有数值都小于 384;有人认为这像是手工挑选的密钥,并用 Dilbert 随机数生成器嘲讽随机源质量极差(较多人) > 这简直是犯罪级的疏忽,8 位密钥太疯狂了(@positive-spite)
怀疑背后是故意行为或内部泄露
- 少数人认为单纯 bug 很难生成第二对同样糟糕的密钥,也没有人会去暴力破解一个几乎全零的私钥,因此怀疑是有人故意植入或内部泄露;还有人表示若很快证实为故意并不意外(少数人) > 若不久后发现这是故意的,我一点也不会意外(@sscaryterry)
认为问题根源在于 CA 信任体系过度膨胀
- 有人指出真正的问题不是这一两个坏密钥,而是浏览器竟然信任上百家 CA,并继续信任那些创建一年以来零签发、也未进入 CT 日志的 CA,暴露出信任链准入与审核机制的失灵(少数人) > 回想起来,受信任的证书颁发机构多达上百家,这件事本身可能就过分了(@jlmorton)
批评报道质量差且带有 AI 文风
- 少数人抱怨这篇 write-up 并不好,只是重复“数字很小”而没有追问成因,还带有自我推广和令人不适的 AI 投稿文风;相比之下昨天讨论的数据更充分(少数人) > 这不是一篇好报道,只说“数字小”而不解释为什么会发生,还带自我推广(@hypfer)
有价值的补充
- 影响评估上,Flatpak 使用所选运行时的内置信任库,更新系统包未必修复应用暴露面;容器也不继承宿主机信任库,而是依赖基础镜像里的信任库(@supernes)
- 有人已公开可用的 .key 文件(GitHub 项目 emudrahahahahaha),便于复现或验证(@joelkoen)
- random.org 的随机性分析被贴出,作为正常随机源质量的对照(@tniemi)
- 有评论将此事类比成“像《三个臭皮匠》闹剧一样的恐怖人祸”,在荒唐中反而让人安心(@tclancy)