Смена SSL-контекста в CPython обрушивает серверы и отключает проверку имени хоста

Python

Две уязвимости в CPython, эталонной реализации языка Python, затрагивают серверную сторону защищённых соединений. Первая позволяет удалённому клиенту без учётной записи обрушить сервер или вызвать код по адресу уже освобождённой памяти. Вторая незаметно отключает сверку имени хоста в сертификате, из-за чего клиент доверяет подложному узлу. Обе закрыты в исправлениях от 30 сентября 2026 года, внешнее уведомление о проблемах появилось днём позже. Клиентские программы затронуты только второй ошибкой.

Детали уязвимостей

Проблему с падением сервера описывает идентификатор CVE-2026-19445 и оценка 9.2 по шкале CVSS 4.0. Ошибка находится в модуле ssl и относится к классу "использование памяти после освобождения". Так называют случай, когда программа продолжает обращаться к объекту, который уже удалён. Злоумышленнику не нужны ни логин, ни пароль: достаточно установить TLS-соединение (протокол защиты транспортного уровня) и указать своё имя сервера в рукопожатии. В рассылке Python Software Foundation уязвимость названа критической, и высокий балл это подтверждает.

Механика опирается на расширение SNI, то есть указание имени сервера при установке защищённого соединения. Серверы используют его, чтобы подобрать подходящий сертификат для каждого домена. В Python за выбор отвечает функция обратного вызова sni_callback. Она подменяет у сокета объект SSLContext, который хранит параметры шифрования и сертификаты. Если после подмены исходный контекст больше ничем не удерживается, память освобождается, а сокет продолжает на неё ссылаться. Обращение к такой памяти даёт сбой процесса либо переход по адресу, где уже лежат другие данные.

Уязвимы серверы, которые создают контекст SSL на каждое соединение или заменяют его при открытых сессиях. Серверы, где один контекст оборачивает слушающий сокет, под удар не попадают. Бюллетень предлагает обходной путь: хранить ссылку на каждый объект SSLContext, которому назначена функция sni_callback, в течение всего времени работы сервера. На практике это означает правку кода в местах, где сертификат выбирается динамически. Ошибка приводит к отказу в обслуживании, а при удачном для атакующего стечении обстоятельств открывает путь к исполнению чужого кода.

Вторая уязвимость, CVE-2026-19553, получила оценку 7.6 и уровень High по классификации Python Software Foundation. Метод wrap_bio() не требовал, чтобы аргумент server_hostname отличался от пустого значения, когда включена проверка имён в сертификате. Проверка параметра в объекте SSL отсутствовала, поэтому сверка имени хоста молча пропускалась. Программа при этом считала, что сертификат проверен и соединение защищено.

Последствия затрагивают не только wrap_bio(). Те же точки входа asyncio.create_connection() и asyncio.loop.start_tls() ведут себя так же. Эти вызовы используют библиотеки и фреймворки, которые строят защищённые каналы поверх asyncio (библиотеки асинхронного ввода-вывода). Клиент, не проверяющий имя узла, принимает любой сертификат, подписанный доверенным удостоверяющим центром. Этого достаточно, чтобы встать между клиентом и сервером и читать трафик. Такой приём называют атакой "человек посередине".

Сверка имени хоста закрывает целый класс атак: без неё подписанный сертификат любого домена годится для подмены. Молчаливый пропуск проверки опаснее явной ошибки, потому что сбой настройки не виден ни в журналах, ни в тестах, пока не случится перехват. Подмену замечают по косвенным признакам, например по изменившемуся сертификату узла. Для сервисов, которые ходят к внешним API, это означает риск утечки ключей и учётных данных.

Исправлять вторую проблему можно без обновления. Достаточно передавать в server_hostname непустое значение, отличное от None. Тогда сверка имени хоста проходит как задумано, и защита работает. Обновление меняет лишь поведение по умолчанию: без имени Python теперь выбрасывает ошибку ValueError, как это давно делает wrap_socket(). Разработчикам полезно проверить код, где аргумент мог не передаваться по недосмотру, и убедиться, что отказ от него не был осознанным решением.

Уязвимы выпуски CPython до 3.12.15, ветка 3.13 до 3.13.16, ветка 3.14 до 3.14.8 и предварительная сборка 3.15.0a1. Финальный выпуск 3.15.0 входит в список исправленных версий. Обновления доступны на сайте проекта и в репозитории. Дистрибутивы Linux и встраиваемые платформы получат патчи отдельно, поэтому одним обновлением системы вопрос закрывается не всегда. Проверить свою ветку стоит и тем, кто не пишет сетевой код: интерпретатор Python входит в состав множества серверных приложений и системных служб.

Практический интерес обе ошибки представляют для многопользовательского хостинга, балансировщиков нагрузки и сервисов, которые подбирают сертификат по домену. Там код с подменой контекста встречается чаще всего. Для клиентских приложений опаснее вторая уязвимость: она создаёт условия для перехвата данных при посреднике в сети. Сведений о реальных атаках с использованием этих уязвимостей в бюллетенях нет, но обе становятся известны публично вместе с исправлениями, так что разбор кода на предмет ошибок начнётся быстро.

Обе проблемы лежат в одном слое, который отвечает за шифрование и подтверждение подлинности узлов. Код, управляющий контекстами и именами хостов, остаётся местом, где малейшая оплошность обнуляет всю защиту соединения. Разумная мера для разработчиков - просмотреть вызовы TLS-интерфейсов, обновить ветку Python и закрепить ссылки на контексты там, где сертификат выбирается динамически. Отдельного внимания заслуживают проекты, которые совмещают балансировку с автоматической выдачей сертификатов: именно в них обе ошибки встречаются в одном и том же коде.

Ссылки

Комментарии: 0