Если у вас есть выпущенное приложение на Flutter под Android, Windows или Linux, ему может понадобиться внеплановый релиз. Команды Dart и Flutter выпустили обновления, которые блокируют скомпрометированные промежуточные сертификаты удостоверяющего центра eMudhra. Для stable это Dart 3.13.7 и Flutter 3.47.8, для beta — Dart 3.14.0-315.3.beta и Flutter 3.50.0-0.2.pre.
Одного обновления SDK на рабочем компьютере недостаточно: приложения и исполняемые файлы нужно заново собрать и доставить пользователям либо развернуть на серверах. Исправление должно попасть в тот код, который действительно выполняет HTTPS-запросы.
Web, iOS и macOS эта конкретная проблема в dart:io не затрагивает. У Linux есть оговорка: риск сохраняется в контейнерах и дистрибутивах, где набор доверенных сертификатов ca-certificates не получает нужные обновления автоматически.
Что случилось с eMudhra
В октябре 2026 года стало известно о компрометации ключей восьми промежуточных сертификатов eMudhra. Причиной оказалась недостаточная случайность при генерации ключей. Закрытые ключи, которые должны быть практически невосстановимыми по открытым данным, удалось восстановить.
Промежуточный удостоверяющий центр имеет право подписывать другие сертификаты. Поэтому последствия выходят далеко за пределы одного сайта: обладатель такого ключа может подделывать сертификаты для произвольных доменов. Клиент, который продолжает доверять соответствующей цепочке, способен принять подделку за настоящий сертификат сервера.
Для атаки требуется возможность вмешаться в соединение между приложением и сервером. Например, злоумышленник контролирует участок сети, через который проходит запрос. Он предъявляет поддельный сертификат нужного домена, приложение принимает его и устанавливает защищённое соединение с подставным узлом. В такой ситуации под угрозой оказываются данные, которые приложение передаёт по этому соединению: токены, пароли, содержимое запросов и ответов.
При этом сертификат вашего собственного API может быть выпущен совсем другим удостоверяющим центром. Проблема возникает на стороне клиента, если тот готов принять подделку, подписанную скомпрометированным ключом.
Почему обновлённая система не всегда защищает приложение
Разработчики браузеров и операционных систем начали распространять экстренные блокировки скомпрометированных сертификатов. Но dart:io на Android, Windows и Linux проверяет цепочки сертификатов через встроенный BoringSSL и не использует системные списки блокировки промежуточных CA.
Здесь легко перепутать две разные вещи: загрузку доверенных корневых сертификатов из системы и саму проверку цепочки. Приложение может брать корни из системного хранилища, но не учитывать отдельный системный запрет на конкретный промежуточный сертификат.
В обсуждении проблемы в репозитории Dart есть показательный тест. Автор проверил 12 тестовых адресов на Samsung с Android 15. Устройство уже получило через Google Play Services список блокировки, содержащий все восемь затронутых ключей. Системный HttpsURLConnection отклонил все 12 соединений с сообщением Certificate blocklisted by public key. Dart HttpClient во Flutter 3.47.0 с Dart 3.13.0 принял все 12 и получил HTTP 200.
То есть на одном телефоне результат зависел от того, какой сетевой клиент выполнял запрос. В том же обсуждении автор сообщил о воспроизведении проблемы на Windows с обновлённым системным списком недоверенных сертификатов.
Поэтому проверка «в браузере этот адрес уже не открывается» ничего не говорит о поведении установленного Flutter-приложения. У них могут быть разные механизмы проверки доверия.
Что изменили в SDK
В обновлённый BoringSSL добавили прямую блокировку скомпрометированных промежуточных сертификатов eMudhra при проверке TLS. Теперь для их отклонения приложение с исправленным SDK не должно полагаться на системный список блокировки.
Второе изменение касается Android 14 и новее. dart:io теперь загружает корневые сертификаты из обновляемого модуля Conscrypt APEX. Это позволяет учитывать изменения корневого хранилища, которые приходят через системные обновления Google Play, включая последующее исключение корневых сертификатов из доверенных.
У этих изменений разное назначение. Прямая блокировка закрывает случай с eMudhra. Переход на Conscrypt APEX даёт доступ к обновляемому корневому хранилищу Android, но не значит, что dart:io теперь использует вообще все системные механизмы отзыва сертификатов.
Что обновлять в своём проекте
Для обычной установки Flutter обновление выполняется через flutter upgrade. Команда скачивает последнюю доступную версию текущего канала, поэтому сначала имеет смысл посмотреть, какой канал выбран:
flutter channel flutter upgrade flutter --version
В выводе проверьте версии Flutter и входящего в него Dart SDK. Отдельная установка нового Dart рядом со старым Flutter не обновит движок, с которым Flutter собирает приложение.
Если версия SDK закреплена в проекте или настройках CI, обновите её и там. Иначе локально уже будет стоять исправленный SDK, а релизный APK или десктопная сборка снова приедет со старого сборщика. Официальная инструкция по обновлению доступна в документации Flutter.
Дальше нужен обычный выпуск приложения: сборка нужных платформ, проверка сетевых сценариев и публикация обновления. Для Android — новый APK или App Bundle; для Windows и Linux — новые дистрибутивы. У пользователей, которые останутся на старой версии приложения, встроенный код сам по себе не заменится.
Для отдельных Dart-программ нужно обновить SDK, которым они собираются или запускаются. Если вы распространяете готовый исполняемый файл, пересоберите и замените его. Если сервис запускается через установленный Dart runtime, обновите среду выполнения и перезапустите сервис.
На Linux отдельно проверьте ca-certificates в фактическом окружении приложения. Контейнер использует собственную файловую систему, поэтому обновление сертификатов на хосте обычно не меняет содержимое уже собранного образа. Для такого развёртывания стоит проверить и версию Dart при сборке, и набор сертификатов в конечном образе.
Обновление зависимостей через pub upgrade эту задачу не решает: исправление находится в SDK. После выпуска проверьте, что в магазин, на сервер или в канал обновлений действительно ушли новые артефакты. Именно их установка завершает обновление.
