已经装在用户手机和电脑上的那一版,证书校验逻辑在打包那一刻就编进去了,本地 SDK 升级只影响你下一次打出来的包。按官方公告的说法,这种旧包还是能被截。eMudhra 这次的事情里,Flutter 的 Android 和 Windows 包正好落在这个位置上:系统和浏览器已经拉黑了那几张中间证书,可 App 自己根本不去问系统。
为什么系统更新救不了它
Dart 官方的安全公告 GHSA-frr6-h895-5pxp 里写着:Android 和 Windows 上,dart:io 发 HTTPS 请求时用的是编进运行时里的 BoringSSL 验证证书链,不读操作系统维护的中间证书黑名单。那八张中间证书的私钥能被算出来,网络路径上的人就可以给任意域名签一张"合法"证书,没修的包会照单全收。
各平台的情况按公告整理一下:
- Android 和 Windows 受影响,必须用新 SDK 重新打包。
- Linux 只在系统
ca-certificates没更新的环境里受影响,比如静态容器或者没打补丁的主机,但官方处置步骤里 Linux 包也是要求重建重发的。 - Web、iOS、macOS 的
dart:io不受影响,它们走浏览器或者 Apple 的Security.framework校验。
修复版本是 Dart 3.13.7 和 Flutter 3.47.8(beta 分别是 3.14.0-315.3.beta 和 3.50.0-0.2.pre)。Flutter 的 CHANGELOG 里 3.47.8 这条写了两件事:一是 Dart 运行时里的 BoringSSL 更新了,能拦住这批中间证书;二是在 Android 14 及以上,dart:io 改成从可更新的 Conscrypt APEX 模块读根证书。后面这条挺实用,以后再有根证书被拉黑,通过 Google Play 系统更新下发就能生效,不用每次都等 App 发版。不过这只对 3.47.8 之后打出来的包有效,老包还是老逻辑。
纯 Dart 写的东西也别漏了,dart compile exe 出来的 CLI、跑在服务器上的 Dart 服务,同样是把校验逻辑编进二进制的,公告里的处置对象写的就是"applications or compiled binaries"。
做了证书固定的项目会好一些:如果用 SecurityContext(withTrustedRoots: false) 只信自己那张 CA,或者固定了服务端公钥,伪造的证书确实过不了这一关,风险小很多。但大多数项目只给主接口做了固定,图片 CDN、第三方 SDK、埋点上报,还有各处零散的 http 调用,还是走默认信任库。另外要搜一下代码里有没有 badCertificateCallback 直接返回 true 的,调试时图省事写的,混进 release 就等于没校验,跟这次的事无关但顺手一起查掉。
用了 Firebase 的再多看一眼
Firebase 出了一份处置指南,有个容易误会的点:iOS 上 dart:io 虽然没事,但用了 Cloud Firestore 的 Flutter 应用,在 Apple 平台和 Windows 上还是受影响,因为插件自带的根证书包也是编进二进制的。指南里的修复版本是 cloud_firestore 7.1.0(FlutterFire BoM 5.1.0 及以上);还没升到新大版本的,可以用回移植的补丁版,iOS/macOS 用 6.10.1(BoM 4.20.1),Windows 用 6.10.2(BoM 4.20.2)。所以 iOS 包也不能一概说不用动,得看依赖里有没有它。
发版这边要做的事
- 本机和 CI 都升到 Flutter 3.47.8,用
flutter --version确认带的 Dart 是 3.13.7 或更高,CI 镜像里钉死的 Flutter 版本尤其要查。 - 用了 Firestore 的,
flutter pub upgrade把cloud_firestore升到上面对应的版本,看一眼pubspec.lock有没有真的变。 - Android、Windows、Linux 的包全部重新构建,别拿旧产物重签。
- 重新上架,各个渠道包、官网直链下载的安装包、企业内部分发的那份都算。
- 推用户更新。有强更或者版本检查接口的,把最低版本号往上提;没有的话,正好借这次加上。Firebase 指南里也写了,操作系统更新和服务端改动都修不了已经装上的旧包。
服务端那边 Node、pip、Docker 镜像和 Java cacerts 的情况,站里另有两帖讲过,这里只说客户端。
最麻烦的大概是那些没做强更、用户又从来不点更新的桌面端。你们打算怎么把这批老用户赶到新版本上?