大家好,我是老刘
今天不少朋友给老刘转发了Flutter 3.47.8修复证书漏洞的消息,问我项目要不要马上升级。
看到私钥可以被直接猜出来,大家紧张很正常。但回到项目里,我们还得回答几个现实问题。自己的App有没有风险?升级后要不要强制用户更新?老项目依赖一堆,能不能稳定升级?
这篇文章把事故讲明白,再告诉你如何判断自己的项目要不要升级。
发生了什么?
2026年10月,HTTPS证书信任体系出了一起严重事故。

印度CA机构eMudhra签发的8张中间CA证书,私钥强度低到可以直接猜出来,其中一个私钥就是数字4。
拿到这些私钥的人,可以给任意域名签出一张看起来合法的HTTPS证书,也就是说你的系统通过HTTPS验证,这就是真实可靠的服务。目前,这些证书已经被吊销。
Flutter随后发布了3.47.8,Dart对应版本为3.13.7。原因很直接,Flutter应用默认的TLS实现没有沿用浏览器和操作系统的那套吊销名单。
私钥怎么会被猜出来?
根据Google安全博客和eMudhra官方声明,事情的时间线如下。
2025年4月30日,eMudhra在FIPS 140-2 Level 3硬件加密机,也就是HSM里执行密钥生成操作,批量生成24张中间CA的密钥。
脚本中的wrap/unwrap密钥导出步骤写错了,HSM里额外生成了一套备用弱密钥。由于密钥标签相似,后续拿去签CSR、提交给各浏览器根证书计划的,恰好是这套弱密钥。
2026年10月17日,安全研究员Wayne用其中8把私钥分别对一段消息完成签名,连同证书一起报告给Apple、Chrome、Microsoft和Mozilla,证明自己已经掌握私钥。
10月18日,eMudhra吊销8张中间证书。
10月20日,Chrome发布CRLSet,Mozilla发布OneCRL,Apple和Microsoft跟进。Chrome同时宣布,将签发这些证书的emSign ECC Root CA - G3根证书从根证书库移除,并于10月27日全量推送。
这8个私钥的实际取值分别是4、56、66、115、155、156、230、237。
看到这组数字,你大概就明白问题有多严重了。
为什么私钥是4会很危险?
P-384椭圆曲线的私钥,本质上是一个位于1到(n)之间的随机整数(d),公钥则是(Q=d\times G),其中(G)是固定基点。
正常情况下,(d)约有(2^{384})种可能,暴力枚举并不现实。
这次情况完全不同。私钥只有3到8 bit,攻击者只要从1枚举到255,逐个计算(d\times G),再和证书中的公钥比对,就能反推出私钥。普通笔记本电脑在一秒内就能完成,不需要特殊设备。
私钥泄露后,攻击者可以自己签一张*.yourbank.com证书,再拼出这样一条证书链。
伪造的叶子证书 → eMudhra中间CA → 根CA。
客户端原本就信任根CA,因此从证书链校验结果来看,这条链完全合法。
一道关键防线发挥了作用
Chrome、Firefox、Edge和Safari强制要求证书进入Certificate Transparency日志,也就是CT日志。
攻击者偷偷签发的证书,一旦进入CT日志就会暴露;不进入CT日志,浏览器又会直接拒绝。
Google明确表示,在封禁之前,这6张TLS中间证书没有签发过任何叶子证书进入CT日志。因此,对强制检查CT的客户端来说,实际遭到攻击的可能性很低。
不过,这其中有运气成分。大量非浏览器客户端,例如语言运行时、命令行工具、容器和嵌入式设备,并不检查CT,只能等待自身更新。
为什么这事会烧到Flutter?
很多人的直觉是,浏览器和操作系统都拉黑名单了,事情应该已经解决了。
对Flutter应用来说,这个推断并不成立。
dart:io的TLS由Dart自己实现。Dart把BoringSSL编进运行时,根证书也随SDK一起打包,来源是Mozilla根证书库,没有走系统信任链。
它不会读取Chrome的CRLSet,也不会读取Windows证书存储中的distrust列表,以及macOS钥匙串里的吊销状态。默认情况下,它同样不进行OCSP或CRL在线吊销检查,也不强制检查CT。
于是就会出现一种很别扭的情况。
浏览器和系统已经拒绝伪造证书,你的Flutter App却仍在使用旧根证书副本,HttpClient依旧可能正常完成TLS握手。
Flutter生态里的package:http和dio,默认都走dart:io,覆盖了绝大多数网络请求。证书虽然已经被吊销,Flutter却感知不到。
浏览器安全,不代表你的App已经安全。客户端用了哪套网络栈,就得看哪套网络栈有没有完成修复。
Flutter 3.47.8修复了什么?
这次修复主要包含两部分。
一是升级内置BoringSSL的证书黑名单,在TLS握手阶段直接拒绝包含这些中间CA的证书链。
二是在Android 14及以上版本,dart:io改为从可更新的Conscrypt APEX模块加载系统根证书。
第二项属于结构性改进。以后Google通过Play系统更新推送根证书撤销,App有机会无需重新发版就能获得修复。
这里还有一个问题,国内的Android系统是否同步更新,大部分厂商应该是会的。
你的项目要不要紧急升级?
先说老刘的判断。
我平时对老项目升级很谨慎,尤其不喜欢为了追新版本,顺手把构建链和插件依赖全部动一遍。但这次涉及TLS证书信任,评估顺序应该反过来。先确认暴露面,再决定升级节奏,平时的版本保守策略不适用于漏洞修复场景。
典型攻击需要先劫持App与服务端之间的网络连接,再利用泄露的CA私钥伪造证书链,让App把伪造的服务端当成真实服务端。
因此,判断风险时主要看两件事。
App是否存在可被劫持的网络请求,以及是否具备独立于CA信任体系的服务端身份验证机制。访问节点的数量和通信协议是否公开,都不是核心判断依据。
你可以按下面三种情况处理。
App会访问多个公开网络服务
这类项目通常涉及不同团队和第三方SDK,临时增加统一保护的成本较高,建议尽快升级。
App只访问自己的服务端,并且具备额外身份验证
如果客户端预置了可信公钥,并且严格验证服务端签名,可以降低部分伪造响应的风险。
但还要继续确认签名覆盖范围、防重放机制,以及数据的机密性和完整性。不能因为接口有token或者响应加了签名,就直接判定安全。
App只访问自己的服务端,但没有额外身份验证
优先升级Flutter并重新发布。暂时无法升级时,可以评估证书锁定或公钥锁定等临时措施,不建议仓促自研一套身份验证协议来替代官方修复。
团队今天就可以做一轮排查。列出线上版本使用的Flutter和Dart版本、实际网络栈、接入的第三方SDK,以及是否具备证书锁定或服务端签名验证。排完这张表,升级优先级基本就清楚了。
升级SDK,只是处理了前半段
站在客户端角度,并不是升级Flutter版本就结束了。
你还得考虑已经在线上运行的App版本怎么处理。
开发分支升到3.47.8,只代表下一个安装包具备修复。用户手机里正在运行的旧版本,仍然带着旧网络栈和旧证书副本。
项目需要结合风险等级、用户更新比例和发版成本,决定采用强制升级、风险提示,还是常规版本迭代。涉及账号、支付或敏感数据的场景,还应该让服务端和客户端一起评估旧版本处置方案。
安全修复的终点,是线上暴露面的真正收缩,不是本地执行一次flutter upgrade。
还有哪些客户端需要跟进?
| 客户端类型 | 状态 |
|---|---|
| Chrome / Firefox / Edge / Safari | 已通过CRLSet / OneCRL / CRLite拉黑,基本安全 |
| Windows / macOS / iOS系统栈,含WKWebView、URLSession | Apple、Microsoft已阻断 |
| Android系统栈 + Google Play服务 | 已通过Play Services更新 |
Flutter / Dart(dart:io) |
必须升至3.47.8 / 3.13.7并重新发版 |
| Go程序 | CVE-2026-111943,1.27.3 / 1.26.10修复,Docker、K8s、Terraform都受到影响 |
| Electron应用,如Slack、Discord等 | 需要等待Electron发布修复版本,再等待应用跟进 |
| Java / Node / curl / 容器镜像 / 嵌入式设备 | 各自更新CA bundle,否则会持续暴露 |
还有个细节值得留意。
8张证书中有2张是Microsoft代码签名证书。这意味着问题不限于伪造HTTPS,理论上还可能被用于伪造带签名的Windows程序。
另外,这8个公钥还被Sectigo使用旧的Comodo AAA根做了交叉签名。同一把钥匙存在两条信任路径,因此两条路径都要分别吊销。
回到开头的问题,Flutter项目要不要紧急升级,答案取决于你的网络暴露面和现有防护措施。但有一件事可以现在就做,先把线上版本、网络栈和第三方SDK查清楚,再安排升级、回归和发版。
别让升级停在开发电脑上,也别因为一次升级可能带来回归风险,就把安全修复无限期往后拖。
你现在的项目停在哪个Flutter版本?评估升级时,卡住你的是插件兼容、发版成本,还是线上旧版本处理?欢迎在评论区聊聊。
🤝 如果看到这里的同学对客户端或者Flutter开发感兴趣,欢迎联系老刘,我们互相学习。
🎁 私信免费领老刘整理的《Flutter开发手册》,覆盖90%应用开发场景。可以作为Flutter学习的知识地图。
💬 : laoliu_dev
📂 老刘也把自己历史文章整理在GitHub仓库里,方便大家查阅。