Три уязвимости в Symfony позволяют остановить приложение одним запросом

Symfony

Три уязвимости в компонентах Symfony позволяют вызвать отказ в обслуживании одним запросом, не проходя аутентификацию. Рейтинг каждой - 8.7 по шкале CVSS 4.0, это высокая степень опасности. Общая черта у всех трёх одна: код разбирает недоверенные данные и не ограничивает ни глубину вложенности, ни сложность вычислений при разборе. Затронуты версии фреймворка на ветках 5.4, 6.x, 7.x и 8.x, причём отдельные пакеты можно подключить и без самого фреймворка. Исправления появились 15 октября 2026 года сразу в четырёх ветках, и в двух случаях первые выпуски оказались неполными.

Разбор YAML, выражений и заголовков в Symfony

Проблемы затрагивают три разных пакета: компонент для работы с YAML, компонент языка выражений и компонент, отвечающий за представление HTTP-запроса. Уязвимости не связаны между собой технически, но используют один и тот же приём - заставляют разборщик уйти вглубь, пока не закончится стек или процессорное время.

Вложенные коллекции в YAML исчерпывают стек

Компонент symfony/yaml читает и записывает YAML (текстовый формат для описания структурированных данных). При разборе встроенных коллекций он переходил на новый уровень рекурсии на каждой открывающей фигурной или квадратной скобке. Ограничение вложенности, которое остальная часть разборщика применяет, на эти вызовы не распространялось. Документ из множества вложенных друг в друга квадратных скобок исчерпывал стек вызовов.

Дальше всё зависит от версии PHP и настроек стека. Процесс либо аварийно завершается, либо запрос заканчивается неустранимой ошибкой. Уязвимость получила номер CVE-2026-109413 и относится к классу неуправляемой рекурсии.

Уязвим любой код, разбирающий YAML из недоверенного источника. В том числе обработчики, которые собирают объект из тела запроса, если в приложении установлен компонент Yaml и не сужен список допустимых форматов. Тогда одного неаутентифицированного запроса с типом содержимого YAML хватает, чтобы уронить рабочий процесс. Разбор теперь входит и выходит на тот же уровень вложенности, что и остальная часть парсера, а слишком глубокие коллекции вызывают исключение о синтаксической ошибке. Исправленные версии - 5.4.54, 6.4.45, 7.4.18 и 8.1.6.

Глубокие выражения обрушивают разбор

Компонент symfony/expression-language строит дерево разбора для вычисляемых условий. Его используют в правилах валидации, маршрутизации и настройках, где значение зависит от других данных. Дерево строилось без ограничения глубины. Выражение из десятков тысяч вложенных унарных операторов, литералов массива, бинарных операторов или обращений к свойствам обрушивало процесс PHP в момент освобождения дерева.

Вектор тот же, что и с YAML: недоверенные данные от пользователя. Если приложение проверяет выражения, которые пишут сами пользователи, одного запроса достаточно для падения. Исправление выпустили в два захода. Первый вариант, попавший в версии 6.4.45, 7.4.18 и 8.1.6, пропустил уровни, которые добавляет цепочка бинарных операторов или обращений к свойствам над первым операндом. Выражение из последовательности сложений внутри шестидесяти уровней скобок всё ещё принималось, а сохранение его в кэш по умолчанию роняло процесс.

Теперь разборщик считает узлы на той ветке, которую строит, и поднимает ошибку синтаксиса после 256 уровней. Дополнительно проверяется глубина уже готового дерева. Обе правки содержатся в версиях 6.4.48, 7.4.21 и 8.1.9, для ветки 5.4 актуальная версия - 5.4.54. Уязвимость описана как CVE-2026-109449.

Пробелы в заголовке Cache-Control съедают процессор

Компонент symfony/http-foundation представляет HTTP-запрос в удобном для приложения виде. Значения заголовков он делит на части с помощью регулярного выражения, и в одной из альтернатив шаблон допускал совпадение с пробельными символами. Из-за этого цепочку пробелов перед разделителем можно было распределить между итерациями повтора экспоненциальным числом способов. Движок регулярных выражений перебирал их все, прежде чем остановиться. Разбор заголовка Cache-Control запускается при создании объекта запроса, поэтому уязвим любой сайт, принимающий HTTP-запросы, без всякой аутентификации.

Цена ошибки измеряется в процессорном времени. Один запрос с таким заголовком занимает почти секунду: 813 миллисекунд для серии из 17 пробелов в заголовке размером 8 КБ на PHP 8.5, и с каждым дополнительным пробелом время удваивается. Начиная с 18 пробелов движок упирается во внутренний лимит на возвраты, и разбор возвращает пустой результат. Все директивы заголовка при этом молча теряются, что само по себе меняет поведение приложения.

Номер уязвимости - CVE-2026-108082, причина - неэффективная сложность регулярного выражения. Первый вариант исправления сломал разбиение по пробельному разделителю: значения, разделённые пробелом, возвращались единой строкой в версиях 6.4.46-6.4.47, 7.4.19-7.4.20 и 8.1.7-8.1.8. Окончательный вариант вошёл в 6.4.48, 7.4.21 и 8.1.9. Пакет http-foundation уязвим начиная с версии 4.1.0, для ветки 5.4 исправление - в 5.4.54.

Что делать администраторам

Ориентир простой: поднять пакеты symfony/yaml, symfony/expression-language и symfony/http-foundation до версий 6.4.48, 7.4.21 и 8.1.9 в соответствующих ветках, а в ветке 5.4 - до 5.4.54. Для http-foundation это обязательно, поскольку предыдущие выпуски либо не закрывали проблему полностью, либо ломали разбор заголовков. Для Yaml формально достаточно 6.4.45, 7.4.18 и 8.1.6, но держать один набор версий проще. Если в проекте установлен метапакет symfony/symfony, он включает все три компонента и тоже требует обновления до тех же версий.

Если обновление отложено, стоит сузить поверхность атаки. Ограничьте список форматов, которые принимает приложение, и не разбирайте YAML и выражения, приходящие от недоверенных пользователей, без предварительной проверки. На периметре можно отсекать запросы с аномально длинными значениями заголовка Cache-Control и длинными сериями пробелов внутри них.

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

Ссылки

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