PHP Armageddon: уязвимость, жившая в ядре десятилетиями

PHP Armageddon: схема уязвимости PHP2Shell в ядре PHP БЛОГ

⚡️ Кратко: ключевые факты и быстрый ответ

  • PHP Armageddon — это две критические уязвимости в ядре PHP: CVE-2026–109618 и CVE-2026–109619 с рейтингом 9.2 из 10, исправления вышли внепланово 21 октября 2026 года.
  • Исследователи назвали связку PHP2Shell: она позволяет удалённо выполнить код на сервере без логина, без пароля и без ошибок в коде самого сайта , только за счёт разбора входного запроса интерпретатором.
  • До установки обновления рабочий минимум , закрыть на уровне веб-сервера приём составных запросов (multipart) и открыть его точечно только на эндпоинтах загрузки файлов.
  • Главный подводный камень: патч закрывает вектор, но не стирает следы. Если сервер был доступен из сети всё это время, логи и каталоги загрузок нужно проверить отдельно.

Что случилось: две критические уязвимости в ядре PHP

21 октября 2026 года команда PHP выпустила внеплановые патчи, закрывающие две критические уязвимости в ядре языка — CVE-2026–109618 и CVE-2026–109619. Обе получили 9.2 балла из 10 по шкале CVSS, то есть формально это «критично» с минимальным зазором до максимума.

Внеплановый релиз для PHP — событие редкое. Обычно ветки обновляются по календарю, с бета-версиями и предупреждениями за месяцы. Здесь же от публикации информации до выхода исправлений прошли считаные дни.

Суть проблемы исследователи упаковали в имя PHP2Shell — «из PHP в шелл». Речь об удалённом выполнении кода на сервере: без логина, без пароля и без единой ошибки в вашем прикладном коде. Дыра сидит в самом ядре интерпретатора, поэтому типовой сайт на фреймворке с аккуратной валидацией ничем не защищён.

⚠️ Подводный камень: «У меня же всё на ORM и никаких eval» здесь не аргумент. Уязвимость отрабатывает раньше, чем управление доходит до вашего кода — на этапе разбора входящего запроса. Отсюда и скорость: единственная реальная мера — обновление самого интерпретатора.

Масштаб оценивают сдержанно и всё равно тревожно. Аналитики MWS AI и Positive Technologies после публикации патчей заговорили о десятках тысяч сайтов в Рунете под потенциальным ударом; официальная рекомендация у обеих сторон одна , ставить обновления немедленно, не дожидаясь планового окна обслуживания.

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

Слово Armageddon в названии дня тоже не случайно. В пентест-сообществе оно давно занято — ещё в 2021 году так называлась машина на Hack The Box, где привилегии поднимали через нашумевший Dirty Sock; тот разбор до сих пор читается как учебник по локальному повышению прав. Нынешняя история масштабнее: там нужен был доступ к машине, здесь , только сеть и уязвимый PHP.

📌 Важно: 9.2 из 10 — это оценка не «сложности эксплуатации», а последствий. Эксплойт не требует учётной записи, значит периметр, закрытый авторизацией, здесь не прикрытие. Считать часы до патча стоит по количеству публичных IP с открытым PHP, а не по важности данных внутри.

В сухом остатке факты простые: две уязвимости, один внеплановый релиз, дата — 21 октября. Что делать дальше и как выиграть время, если обновиться прямо сейчас нельзя, разберём ниже.

Как ИИ нашёл дыры, которые жили десятилетиями

Методика выглядит буднично: языковые модели читают код интерпретатора и историю коммитов, выдвигают гипотезы об опасных путях исполнения, а мутационные фаззеры проверяют каждую гипотезу потоком входных данных. Ничего мистического, просто два инструмента наконец научились разговаривать друг с другом.

Раньше эти два лагеря жили отдельно. Фаззеры вроде AFL++ или libFuzzer отлично отрабатывали на бинарниках, где покрытие измеряется сантиметрами машинного кода. Но у интерпретатора вход — это не байты, а текст программы. Чтобы добраться до интересной ветки, фаззеру нужно сгенерировать синтаксически валидный скрипт, который пройдёт парсер и вызовет нужную функцию ядра. Слепые мутации до таких глубин почти не доходят.

Статический анализ страдал от зеркальной проблемы. CodeQL, Semgrep и компиляторные предупреждения ищут по правилам, а правила кто-то должен написать руками. Если разработчик не подозревает, что обработка входящего запроса где-то теряет проверку границ, то и соответствующего правила в базе не появится. Дыра спокойно живёт десятилетиями — до первого, кто догадается посмотреть именно в это место.

Этап конвейера Классический подход С участием LLM
Поиск подозрительных мест Правила, написанные вручную Модель читает диффы и старые коммиты, ранжирует участки по риску
Гипотеза об уязвимости Опыт и интуиция аудитора Модель описывает сценарий эксплуатации словами и кодом
Проверка гипотезы Слепой фаззинг с покрытием Фаззер получает валидные затравки под конкретную ветку
Отсев мусора Ручной разбор срабатываний Модель второго прохода объясняет, почему путь недостижим

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

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

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

Так что ИИ здесь не «нашёл» в смысле озарения. Он сыграл роль очень начитанного напарника, который прочитал все диффы за последние годы и подсказал, куда смотреть. Проверял всё равно инструмент. Если хотите понять, как устроена работа таких моделей с контекстом и внешними инструментами, у нас есть отдельный разбор, как работает ИИ-агент: токены, контекст и MCP на пальцах.

📌 Важно: связка «LLM + фаззинг» снижает порог входа в аудит сложных кодовых баз, но не отменяет правило: находка считается находкой только после воспроизводимого падения с понятным стеком вызовов.

Под капотом PHP2Shell: как работает удалённое выполнение кода

PHP2Shell — это цепочка из двух дыр в ядре интерпретатора, где разбор входящего запроса приводит к выполнению чужого кода с правами веб-сервера. Логин не нужен: уязвим сам слой, который принимает данные ещё до вашего скрипта.

Самое неприятное тут — момент срабатывания. Код приложения ещё не начал выполняться, а интерпретатор уже разбирает тело запроса и заголовки. Всё, что вы написали в index.php, в этот момент вообще не имеет значения.

Давайте разберём цепочку по шагам, без готовых эксплойтов — сути это не меняет, а копировать рабочие полезные нагрузки из статьи всё равно не стоило бы.

Шаг 1. Разведка. Атакующий ищет, где вообще живёт PHP: заголовки ответа, характерные страницы ошибок, поведение при кривых запросах. Задача — понять версию и то, как запросы доходят до интерпретатора: напрямую, через обратный прокси или через отдельный пул PHP-FPM.

Шаг 2. Формирование запроса. Собирается составной запрос с нестандартной структурой: необычные границы частей, вложенные метаданные, несоответствие заявленной длины реальному телу. Ничего экзотического — обычный HTTP, который пройдёт через любой роутер.

Шаг 3. Триггер парсера. Интерпретатор начинает разбор тела запроса и попадает в уязвимый участок ядра. Здесь и срабатывает первая дыра: логика разбора выходит за границы ожидаемого буфера.

Шаг 4. Управление памятью. Вторая уязвимость превращает повреждение в контролируемое: подменяются внутренние структуры, и дальнейшее исполнение идёт по сценарию атакующего. Именно связка двух багов даёт название «из PHP в шелл».

Шаг 5. Выполнение. Полезная нагрузка исполняется с правами пользователя, под которым работает веб-сервер или пул обработчиков. Оттуда — чтение конфигураций, доступ к базе, запись файла в каталог загрузок для закрепления.

Обратите внимание на масштаб: приложение может быть написано идеально, с подготовленными выражениями и валидацией. Дыра всё равно сработает, потому что она этажом ниже. Это ровно та ситуация, когда защита периметра важнее чистоты кода , тот же принцип минимальных привилегий, что мы разбирали в материале про права доступа ИИ-агента: разбор, почему 120 вместо 100.

⚠️ Подводный камень: главное ограничение PHP2Shell , запрос обязан дойти до интерпретатора в составном виде (multipart/form-data). Рекомендации по экстренному смягчению, разлетевшиеся по инженерным чатам 21–22 октября, сходятся на одном: резать такие тела на веб-сервере и разрешать их только на маршрутах загрузки файлов. Если перед PHP стоит прокси, отбрасывающий составные тела на всех остальных путях, атакующий остаётся ни с чем.

Второе ограничение — сетевая доступность. Сервис за NAT, без внешнего маршрута к уязвимому обработчику, эксплуатировать снаружи не получится. Но это слабое утешение для публичных сайтов.

Что это даёт на практике? Оборона сводится к двум действиям: как можно быстрее обновить интерпретатор и поставить фильтр на составные запросы. Подробный разбор инцидента и официальные рекомендации команды PHP собраны в материале «PHP взломали! Обороняемся» — там же указаны конкретные ветки, для которых вышли внеочередные сборки.

Сравнение способов защиты: патч, nginx, WAF и изоляция

Быстрее всего закрывает дыру правило на веб-сервере: 15–30 минут и перезагрузка конфигурации. Надёжнее всего — обновление ядра PHP, потому что оно убирает саму уязвимость, а не её проявления. WAF и изоляция работают как страховка на время, пока патч не доехал до всех серверов.

Именно поэтому вопрос «что выбрать» обычно ставится неправильно. Выбирать нужно не одну меру, а порядок действий. Сначала жгут, потом лечение, и только затем , антибиотики на постоянку.

Мера Время внедрения Что именно закрывает Цена ошибки Остаточный риск
Патч ядра PHP 2–4 часа на тестовом контуре, 1–3 дня на парке серверов Корень проблемы: сам механизм удалённого выполнения кода Ломается совместимость сторонних расширений, нужен откат и повторное тестирование Низкий по конкретной уязвимости, но остаются старые сборки и забытые тестовые стенды
Правило в nginx 15–30 минут Вектор доставки вредоносного запроса , например, точечная блокировка составных запросов Отваливаются загрузки файлов, аватарки и приёмы вложений через API Средний: обход через разрешённые точки приёма файлов и другие протоколы
WAF 30 минут на включение, от суток на настройку правил Сигнатуры известных атак и аномальное поведение запросов Ложные срабатывания на легитимном трафике, блокировка живых клиентов Средний: обфускация запросов и внутренний трафик вне зоны фильтрации
Изоляция (контейнер, непривилегированный пользователь, read-only файловая система) Недели: меняется архитектура и права Не саму уязвимость, а последствия: размах доступа после пробития Рост сложности эксплуатации, проблемы с правами на запись в кэш и логи Уязвимость остаётся, но превращается из «сервер наш» в «упал один контейнер»

Обратите внимание на асимметрию в последнем столбце. Патч закрывает причину, всё остальное — только окно возможностей. Это не значит, что остальное не нужно. Это значит, что остальное не заменяет обновление.

⚠️ Подводный камень: самое популярное правило «запретить все составные запросы целиком» ломает половину сайта. Формы с файлами, редакторы в админке, мобильные клиенты с загрузкой фото — всё это уходит в ошибку. Правило нужно ставить в режим логирования, собрать реальные точки приёма файлов за сутки и только потом запрещать остальное.

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

С изоляцией та же история. Контейнер с правами root внутри и смонтированным сокетом оркестратора даёт ровно ноль выигрыша. Прежде чем гордиться контейнеризацией, проверьте, от какого пользователя стартует процесс и что висит в томах , мы разбирали похожую логику ограничения прав в материале про права доступа ИИ-агента.

💡 Инсайт: рабочий порядок такой , сегодня правило в nginx и включённый WAF в режиме наблюдения, на этой неделе патч на канареечную группу серверов, в течение месяца изоляция для самых публичных морд. Патч обязан быть первым в очереди по срокам, а не последним по важности.

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

Как закрыть сервер до патча: блокируем multipart на nginx

Полный запрет составных запросов на nginx делается одной проверкой заголовка Content-Type в директиве map и возвратом кода 403, а точечное разрешение — узким белым списком адресов загрузки, где multipart остаётся рабочим.

Логика простая. Разбор составного тела — отдельный парсер внутри интерпретатора, и он включается ровно в тот момент, когда в заголовке стоит multipart/form-data. Нет заголовка — нет парсера. Значит, резать надо на входе, ещё до того, как тело дочитается до конца и попадёт во временный файл.

Собираем три карты: признак составного запроса, список доверенных точек загрузки и итоговое правило запрета.

# 1. Признак составного тела — только по заголовку
map $http_content_type $is_multipart {
    default                  0;
    "~*^multipart/form-data" 1;
}

# 2. Белый список точек загрузки — строго по $uri
map $uri $is_upload_zone {
    default               0;
    "/api/v1/avatar"      1;
    "/api/v1/attachments" 1;
}

# 3. Запрет срабатывает только там, где multipart не ждут
map "$is_multipart$is_upload_zone" $deny_multipart {
    default 0;
    "10"    1;
}

server {
    listen 443 ssl;
    server_name example.com;

    client_max_body_size 1m;

    if ($deny_multipart) {
        return 403;
    }
}

Конструкция с `if` в контексте server выглядит подозрительно, но здесь это единственный безопасный сценарий: внутри блока ровно один `return`, никакой другой логики. Проверка отрабатывает в фазе server rewrite, то есть раньше, чем nginx вообще начнёт читать тело запроса. Мусорный поток от бота отваливается на первом же пакете.

📌 Важно: Регулярка ~* нечувствительна к регистру, поэтому MultiPart/Form-Data тоже отсекается. А вот параметр boundary в заголовке проверке не мешает — якорь стоит только на начале строки.

Теперь исключение. Точка загрузки должна совпадать с `location =` побайтово, иначе белый список разъедется с реальной маршрутизацией.

location = /api/v1/avatar {
    if ($is_multipart = 0) { return 415; }

    limit_except POST { deny all; }
    client_max_body_size 2m;
    client_body_buffer_size 128k;
    client_body_timeout 20s;

    include fastcgi_params;
    fastcgi_pass unix:/run/php/php-fpm.sock;
}

Обратная проверка на 415 необязательна, но полезна: точка загрузки должна принимать только то, ради чего её открыли. Лимит размера и таймаут тела закрывают второй сценарий , медленную отправку, которая держит воркер открытым часами.

⚠️ Подводный камень: Заголовок , не единственный вход в парсер. Если ваш код или сторонняя библиотека сами разбирают php://input как составное тело, не глядя на Content-Type, защита не сработает. Проверяйте гипотезу на стенде, а не на продакшене.

Перед включением соберите карту загрузок по логам. Добавьте `$content_type` в формат и прогоните историю за пару недель.

log_format with_ct '$remote_addr "$request" "$http_content_type" $status';

awk -F'"' '$4 ~ /multipart/ {print $2}' /var/log/nginx/access.log \
  | sort | uniq -c | sort -rn | head -40

💡 Инсайт: Тот же принцип минимальных привилегий, что мы разбирали в материале про права доступа ИИ-агента и разницу между 120 и 100, работает и здесь: не «разрешить всем, кроме плохих», а «разрешить ровно двум адресам из тысячи». Список из двух строк проще проверить глазами, чем конфиг на три экрана.

И трезвая мысль напоследок. Блокировка на nginx — это жгут, а не лечение: она выигрывает часы или сутки, пока выкатывается обновление интерпретатора. Ломается при этом многое — визуальные редакторы CMS, формы с вложениями, вебхуки платёжек. Поэтому включайте в режиме наблюдения, сверяйтесь с логами и снимайте правило сразу после установки патча.

Что делать после обновления: аудит логов и поиск веб-шеллов

Аудит после обновления — это три обязательных шага: поиск следов эксплуатации в журналах веб-сервера, охота за веб-шеллами в каталогах загрузок и полная ротация всех секретов, до которых мог дотянуться чужой код на сервере.

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

Начните с журналов доступа. Нам нужны записи минимум за две–четыре недели до даты выхода исправлений, включая архивные и сжатые файлы — их читают через zgrep, иначе картина будет неполной.

Ищите запросы методом POST с типом содержимого multipart на адреса, где загрузки никогда не планировались: главная, страницы каталога, обработчики форм обратной связи. Отдельный признак — ответы с кодом 200 на запросы к файлам .php внутри папок для картинок и документов. Нормальный сайт так себя не ведёт.

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

Маркер в коде О чём говорит
eval, assert, create_function Выполнение кода из строки, типичная обвязка шелла
base64_decode, gzinflate, str_rot13 Скрытие полезной нагрузки от глаз администратора
$_POST, $_REQUEST с длинными именами Приём команд извне под видом обычного поля формы
.user.ini, .htaccess с auto_prepend_file Автоподключение чужого файла ко всем страницам сайта

Файлы нашли — не спешите радоваться. Удаление шелла без ротации секретов бессмысленно: злоумышленник уже прочитал .env, доступы к базе данных и ключи внешних интеграций.

⚠️ Подводный камень: смена пароля администратора ничего не даёт, если остались живыми ключи платёжного шлюза, токены CI/CD и секрет подписи сессий. Меняйте всё сразу и принудительно завершайте активные сессии — иначе чужой вход восстановится за минуты.

Теперь точки загрузки. Запретите исполнение PHP в каталогах для пользовательских файлов на уровне веб-сервера, храните вложения вне корня сайта и проверяйте двойные расширения вроде image.php.jpg. Контейнеры проще пересобрать из чистого образа, чем вычищать вручную.

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

Кто под ударом: оценка рисков для сайтов Рунета

Под ударом оказались десятки тысяч сайтов Рунета на PHP без свежих патчей: такую оценку потенциального охвата дали эксперты MWS AI, а Positive Technologies призвали пользователей немедленно установить обновления ядра языка.

Цифра «десятки тысяч» звучит солидно, но на практике это скорее нижняя граница, чем потолок. PHP держит на себе огромный пласт сайтов с готовыми системами управления контентом, и далеко не все владельцы вообще знают, какая версия интерпретатора крутится у них на сервере.

Причина проста: обновление ядра — это не кнопка «Обновить» в панели. Кто-то должен зайти по SSH, проверить совместимость расширений, прогнать регрессию и перезапустить сервисы. У команд из двух человек на это обычно нет ни времени, ни свободного окна в графике релизов.

⚠️ Подводный камень: «Мы обновились» почти никогда не означает «мы обновились везде». Забытые копии на тестовом контуре, старые образы в реестре контейнеров, замороженные проекты клиентов и сайты на общем хостинге, где версию интерпретатора задаёт провайдер, , вот где дыра живёт месяцами после официального патча.

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

Сегмент Реальный уровень риска Что делать первым делом
Собственный сервер, свой DevOps Средний: патч доступен, всё зависит от скорости реакции Обновить ядро, проверить логи на аномальные запросы
Общий хостинг Высокий: версию задаёт провайдер Написать в поддержку, забрать письменный ответ со сроками
Корпоративный портал на legacy-стеке Критический: старые расширения не собираются на новом ядре Временная блокировка обработки составных запросов на веб-сервере
Замороженные и тестовые копии Критический: о них просто забыли Инвентаризация, затем выключение или изоляция от сети

💡 Инсайт: для небольшого проекта самый быстрый выигрыш — не ждать патча, а закрыть обработку составных запросов там, где вы не принимаете файлы. Это занимает минуты и не требует пересборки приложения. Логика ИИ-сканеров, которые ищут подобные бреши по шаблонам, разобрана в материале как работает ИИ-агент: токены, контекст и MCP на пальцах — полезно, чтобы понимать, по каким признакам вас находят.

Наш опыт показывает: при массовых уязвимостях в ядре языка страдают не самые крупные, а самые незаметные. Крупные компании хотя бы имеют регламент обновлений, а небольшой проект держится на человеке, который в отпуске.

Вывод простой: посчитайте не сайты, а копии своего кода в продакшене. Если ответ вас удивил, начинать нужно не с патча, а с инвентаризации.

Чек-лист: что сделать в первые 24 часа

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

Окно Действие Критерий готовности
0–2 ч Инвентаризация: php -v на каждом хосте, внутри контейнеров и на сборках из CI Есть список «хост , ветка , владелец»
2–4 ч Сузить внешний вектор на nginx до обновления Загрузка файлов разрешена только на явных эндпоинтах
4–8 ч Обновление из репозитория дистрибутива или официальной сборки, перезапуск php-fpm php -v показывает нужную сборку, OPcache сброшен
8–16 ч Аудит логов и файловой системы на следы закрепления Нет свежих файлов в каталогах загрузок, нет странных вызовов
16–24 ч Ротация секретов, урезание прав, план миграции старых веток Доступы обновлены, отставшие ветки в плане

Самая частая ошибка , «обновили пакет и забыли». На практике выяснилось: в контейнерной среде apt upgrade на хосте не меняет слой образа, а старый процесс php-fpm продолжает крутить уязвимый код в памяти.

Поэтому перезапуск php-fpm и сброс OPcache — не формальность, а обязательный шаг. Проверяйте сборку изнутри окружения, а не по статусу пакетного менеджера.

⚠️ Подводный камень: репозитории дистрибутивов и зеркала синхронизируются с задержкой, а часть веток PHP уже вне поддержки и патча для них не будет вовсе. Не ориентируйтесь на «пакет обновился без ошибок» , сверяйте реальные номера сборок на php.net, там же видно, какие ветки ещё получают исправления безопасности.

Отдельная строка расходов — зависимости. Фреймворки и CMS тянут свой рантайм, и иногда прод тянет устаревшую ветку через композер, а не через систему. Пробегитесь по манифестам и образам: расхождение версий между хостами — норма для легаси-парков, но не в день инцидента.

Заодно урежьте права пользователя, под которым работает php-fpm: логика та же, что и с доступами ИИ-агентов — лишние разрешения просто расширяют зону поражения (у нас был подробный разбор минимальных привилегий).

💡 Инсайт: заведите привычку хранить в заметках раннера прямую ссылку на страницу релизов php.net и checksum-суммы сборок. В момент паники вы не будете искать, какая версия считается закрытой, — вы просто откроете официальный список и сравните с выводом php -v.

И последнее: сутки — это не дедлайн, а старт. Проверку логов и файлов имеет смысл повторить через 48 и 72 часа, потому что закрепление в системе редко бывает одноразовым.

Часто задаваемые вопросы (FAQ)

Что именно взломали — мой сайт на WordPress или сам язык PHP?

Сам интерпретатор. Речь о двух критических уязвимостях в ядре языка — CVE-2026–109618 и CVE-2026–109619, обе по 9.2 балла из 10. Исследователи дали им имя PHP2Shell: сценарий разворачивается от「входной запрос」до「шелл на сервере」.

Ключевой момент — удалённое выполнение кода проходит без логина, без пароля и без единой ошибки в вашем коде. Ни свежий движок сайта, ни аккуратные плагины тут не защищают: дыра лежит ниже, на уровне разбора входящих данных.

Внеочередной релиз с исправлениями команда PHP выпустила 21 октября 2026 года — само по себе событие редкое, обычно патчи ждут планового окна. То есть чинили в режиме пожара.

Как за пять минут понять, под ударом ли мой сервер?

Начните с фактов, а не с догадок. На хосте — php -v, в панели управления — селектор версии, в контейнере — тег в Dockerfile и вывод docker inspect. Смотрите не на то, что «должно стоять», а на то, что реально исполняет запросы.

Классическая ловушка: на сервере три версии PHP, сайт крутится на самой старой, а обновляли вы вторую. Проверяйте каждое приложение отдельно, включая тестовые контуры, legacy-проекты и старый VPS, куда год никто не заходил.

И заодно уберите заголовок X-Powered-By — он бесплатно рассказывает сканерам, чем вы дышите.

Обновление страшно ставить в прод, как не уронить сайт?

Патч безопасности не откладывают на «после праздников», но и не заливают вслепую. Схема простая: сначала тестовый контур с копией боевых данных, затем прогон ключевых сценариев — вход в аккаунт, оплата, загрузка файлов, отправка форм.

Дальше канареечный выпуск: один узел за балансировщиком получает обновление, остальные работают на прежней сборке. Держите под рукой сохранённый предыдущий пакет или образ — откат должен занимать минуты, а не часы.

⚠️ Подводный камень: пока вы согласовываете окно, уязвимость уже описана публично. Каждая неделя промедления — это неделя, когда сканеры перебирают ваш диапазон адресов. Первые 30–60 минут после выпуска следите за ошибками 5xx и временем ответа, дальше будет спокойнее.

Можно ли прикрыться nginx и WAF и спокойно жить до обновления?

Частично. Компенсирующие меры реально снижают риск и покупают время: заблокируйте составные запросы с файлами (multipart) на всех адресах, где загрузка файлов не предусмотрена, ограничьте максимальный размер тела запроса, включите виртуальные правила в WAF.

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

Но честно: это выигрыш времени, а не решение. Универсальное регулярное выражение, которое «отсечёт всё», обязательно обойдут — не сегодня, так через неделю.

Как понять, что сервер уже скомпрометировали?

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

Третья — связи и закрепление: исходящие соединения на незнакомые адреса, правки в планировщике задач, новые ключи в ~/.ssh/authorized_keys, изменения в .htaccess, записи в логах о составных запросах на индексный файл.

Если хоть что-то совпало — изолируйте узел, снимите образ диска для разбора, смените пароли и ключи доступа к базе, пересоберите сервер из чистого образа. Искать malicious-файлы вручную по всей файловой системе смысла мало: заражённый сервер проще пересоздать.

💡 В сухом остатке: главное

Рынок технологий движется с бешеной скоростью. Не ведитесь на маркетинговый хайп: всегда тестируйте модели на реальных сценариях вашей команды и замеряйте финальную окупаемость.

Чек-лист для внедрения на практике:

  • ✅ Уточните на официальном сайте PHP точные версии с исправлением для вашей ветки и обновите интерпретатор, а не только код проекта
  • ✅ До обновления заблокируйте multipart-запросы на nginx или другом фронтенде, оставив исключения лишь для реальных точек загрузки файлов
  • ✅ Проверьте логи доступа за последние сутки на подозрительные составные запросы, а также каталоги uploads на посторонние исполняемые файлы
  • ✅ После патча смените секреты приложения, ключи API и пароли сервисных учётных записей, которые могли быть доступны с сервера
  • ✅ Зафиксируйте в регламенте срок реакции на критический патч и владельца этой процедуры, иначе следующее обновление снова пройдёт мимо

Обсудить материал и поделиться своими бенчмарками можно в комментариях к блогу.

Редактор
Оцените автора
Добавить комментарий

Отправляя данную форму вы соглашаетесь с политикой конфиденциальности сайта