Критические уязвимости PHP: обновление Docker-образа и автоматический деплой
Работаешь себе спокойно, пишешь код, исправляешь баги, иногда борешься с Docker, а потом узнаёшь, что в PHP обнаружили критические уязвимости, которые потенциально позволяют выполнять произвольный код на сервере без авторизации.
И вот тут начинается самое интересное: проблема может быть не в твоём коде, не в Laravel и даже не в Composer-зависимостях. Уязвимым может оказаться сам PHP runtime.
В этой статье разберёмся, что произошло, какие версии PHP содержат исправления, чем опасно игнорирование обновлений и как я обновила PHP в своём проекте. А заодно расскажу, почему однажды настроенный CI/CD особенно радует именно в такие моменты.
Что произошло?
21 октября 2026 года были опубликованы обновления безопасности PHP, устраняющие две критические уязвимости удалённого выполнения кода — Remote Code Execution (RCE).
RCE — это класс уязвимостей, при эксплуатации которых злоумышленник может добиться выполнения произвольного кода на целевой системе. В зависимости от сценария атаки и настроек окружения это может привести к компрометации приложения или сервера.
Речь идёт о двух уязвимостях:
- CVE-2026-109618 — проблема, связанная с обработкой исчерпания памяти и внутренними структурами PHP.
- CVE-2026-109619 — проблема, связанная с использованием устаревшего внутреннего состояния и обращением к памяти, которая уже не должна использоваться.
В определённых условиях эти ошибки могут привести к повреждению памяти и потенциальному выполнению произвольного кода.
Важно понимать: наличие уязвимости не означает, что любой сервер автоматически будет взломан. Для эксплуатации имеют значение конкретные условия, конфигурация и доступность уязвимого кода. Но если опубликовано исправление критической уязвимости, откладывать обновление без веской причины — сомнительная стратегия.
Проверить информацию можно в официальном разделе безопасности PHP и в базе CVE.
В каких версиях PHP исправили уязвимости?
Для исправления необходимо использовать версию, содержащую соответствующие патчи.
Ветка PHP | Версия с исправлениями |
|---|---|
PHP 8.2 | 8.2.35 |
PHP 8.3 | 8.3.36 |
PHP 8.4 | 8.4.27 |
PHP 8.5 | 8.5.12 |
Перед обновлением конкретного окружения необходимо свериться с официальными рекомендациями безопасности и убедиться, что выбранный Docker-образ действительно содержит исправления.
Также важно учитывать жизненный цикл PHP: старые неподдерживаемые ветки могут не получать исправления от самого проекта PHP.
Что будет, если не обновить PHP?
Представим обычное веб-приложение. Пользователь отправляет HTTP-запрос, Nginx передаёт его PHP-FPM, а PHP обрабатывает запрос и запускает код приложения.
Если используемая версия PHP уязвима, злоумышленник при подходящих условиях может попытаться эксплуатировать ошибку на уровне самого интерпретатора.
При успешной атаке возможны следующие последствия:
- выполнение произвольного кода с правами PHP-процесса;
- доступ к данным приложения и конфигурационным файлам, доступным этому процессу;
- получение секретов из файлов или переменных окружения;
- использование доступных учётных данных для дальнейшего проникновения в инфраструктуру;
- изменение файлов, нарушение работы приложения и другие последствия компрометации.
Масштаб ущерба зависит от прав процесса, настроек контейнера, доступных секретов и изоляции инфраструктуры.
Отдельно стоит отметить: Nginx перед PHP-FPM сам по себе не защищает от уязвимости в PHP. Если запросы обрабатываются уязвимым интерпретатором, наличие reverse proxy не устраняет проблему.
Поэтому своевременное обновление runtime — такая же часть поддержки приложения, как обновление библиотек и исправление ошибок в собственном коде.
Как я обновила PHP в своём Docker-проекте
Мой проект работает в Docker Compose. В Dockerfile используется многоэтапная сборка — multi-stage build.
На первый взгляд Dockerfile выглядит довольно внушительно, но после изучения структуры стало понятно, что для обновления PHP не нужно переписывать приложение или менять архитектуру.
1. Разобралась в Dockerfile
В проекте используются четыре стадии сборки:
- `assets` — сборка фронтенда на Node.js;
- `builder` — установка PHP-расширений и Composer-зависимостей;
- `production` — финальный образ приложения;
- `development` — образ для разработки, наследующий production и дополнительно устанавливающий Xdebug.
Для PHP использовался базовый образ:
FROM php:8.4-fpm AS builderИ ещё один:
FROM php:8.4-fpm AS productionЗдесь важно не пропустить вторую строку. Обновить только `builder` недостаточно, если финальный production-образ продолжает использовать старую версию PHP.
Стадия `development` наследуется от `production`, поэтому отдельный базовый PHP-образ для неё не требуется.
2. Обновила базовый образ PHP
Для ветки PHP 8.4 исправленная версия — 8.4.27.
Я заменила обе строки в Dockerfile:
FROM php:8.4.27-fpm AS builderFROM php:8.4.27-fpm AS productionТаким образом, исправленная версия PHP используется и на этапе сборки зависимостей, и в финальном production-образе.
При этом не пришлось менять код приложения, переписывать контроллеры или обновлять Laravel ради устранения этих уязвимостей.
Разумеется, после изменения базового образа важно проверить успешность сборки и совместимость приложения с обновлённым окружением.
3. А дальше за меня работает CI/CD
Вот здесь начинается моя любимая часть истории.
Мне не пришлось вручную заходить на сервер, выполнять `docker compose build`, собирать образ и заниматься его доставкой в production. В проекте уже настроен автоматизированный процесс с использованием GitHub Actions, приватного Docker Hub и Ansible.
Процесс выглядит следующим образом:
1. Я изменяю Dockerfile и отправляю изменения в ветку `master`.
2. GitHub Actions запускает сборку Docker-образа в соответствии с настроенным workflow.
3. Собранный образ публикуется в приватный Docker Hub.** Образ приложения хранится в закрытом registry, а не в публичном репозитории.
4. Ansible доставляет обновление на production. Он подтягивает опубликованный образ и выполняет предусмотренные конфигурацией действия по развёртыванию приложения.
В итоге путь от изменения Dockerfile до обновления приложения на сервере автоматизирован.
Мне не нужно помнить последовательность команд для ручного деплоя или каждый раз самостоятельно собирать образ на production-сервере. Я вношу изменение в репозиторий, а настроенный процесс CI/CD выполняет свою работу.
Конечно, автоматизация не отменяет проверки: после обновления необходимо убедиться, что на production действительно запущена исправленная версия PHP, а приложение работает корректно.
Но именно в такие моменты особенно хорошо видно, насколько полезна инфраструктура, которую однажды правильно настроили.
Я не просто обновила версию PHP — я воспользовалась уже работающим процессом доставки изменений, чтобы не заниматься рутинными операциями вручную.
4. Проверила результат
После развёртывания важно убедиться, что контейнер действительно использует новую версию PHP, а не продолжает работать на старом образе.
Для этого можно выполнить:
docker compose exec php php -vЗдесь `php` — пример имени сервиса из Compose-файла.
В выводе должна отображаться исправленная версия PHP или более новая версия той же ветки, содержащая необходимые патчи.
Дополнительно стоит проверить расширения:
docker compose exec php php -mВ моём проекте используются `pdo_mysql`, `pdo_pgsql`, `intl`, `zip`, `redis`, `opcache` и другие расширения. После обновления важно убедиться, что все необходимые модули доступны.
И, конечно, проверить само приложение: запуск, API, подключение к базе данных и остальные критические сценарии.
Достаточно ли обновить только PHP?
Для устранения конкретных уязвимостей PHP необходимо установить версию с соответствующими исправлениями. Однако это не означает, что после обновления весь Docker-образ автоматически становится безопасным.
В проекте остаются другие компоненты:
- Nginx — отдельный компонент со своими обновлениями безопасности.
- Node.js — используется для сборки фронтенда.
- Системные библиотеки — устанавливаются через пакетный менеджер и также могут содержать уязвимости.
- Composer-зависимости — сторонние PHP-пакеты необходимо проверять отдельно.
Для проверки Composer-зависимостей можно использовать:
composer auditДля сканирования Docker-образов на известные уязвимости подойдёт Trivy.
Такие проверки позволяют обнаруживать проблемы не только в PHP, но и в других компонентах окружения.
Обновление runtime закрывает конкретную проблему, но безопасность приложения — это непрерывный процесс, а не одноразовая задача.
Выводы
Эта история напомнила мне несколько вещей.
Во-первых, безопасность приложения зависит не только от кода, который мы пишем. Базовые Docker-образы, интерпретатор, системные библиотеки и сторонние зависимости тоже требуют внимания.
Во-вторых, multi-stage Dockerfile нужно читать внимательно. Обновить одну строку в одной стадии не всегда достаточно: важно понимать, какой образ в итоге запускается на production.
В-третьих, исправление уязвимости не обязательно требует большого рефакторинга. Иногда нужно разобраться в структуре сборки, выбрать исправленную версию, обновить базовый образ и проверить результат.
И наконец, я ещё раз убедилась, что хорошо настроенный CI/CD — это не просто удобство, а реальная помощь в работе с инфраструктурой.
Я меняю Dockerfile, отправляю изменения в `master`, GitHub Actions собирает образ, приватный Docker Hub хранит его, а Ansible доставляет обновление на production. Никакой ручной сборки на сервере.
Недостаточно просто знать, какую версию PHP нужно установить. Хорошо ещё настроить процессы так, чтобы необходимые изменения можно было безопасно и предсказуемо доставлять до production.
Backend-разработка — это не только контроллеры, SQL-запросы и исправление багов. Это ещё и понимание того, как приложение собирается, запускается, обновляется и защищается.
А когда в нужный момент инфраструктура работает так, как задумано, можно спокойно сказать: горжусь собой :)
И да, автоматизация, которую ты однажды настроила, особенно радует тогда, когда нужно оперативно закрыть уязвимость. 💅