
RCE в PHP: как работает уязвимость и как защитить сайт
RCE в PHP — уязвимость, позволяющая выполнить произвольный код на сервере. Разбираем причины, примеры и меры защиты сайта.
Удалённое выполнение кода в PHP: что это за уязвимость и как от неё защититься
Удалённое выполнение кода (RCE, Remote Code Execution) в PHP — это уязвимость, при которой внешний пользователь через HTTP-запрос заставляет сервер исполнить произвольный PHP-код или команды операционной системы. Защита строится на трёх основах: не передавать пользовательские данные в опасные функции, своевременно обновлять PHP и CMS, ограничивать права процесса на сервере.
Из статьи вы узнаете, как возникает RCE в PHP-приложениях, какие функции и сценарии приводят к проблеме, какие реальные уязвимости уже находили в экосистеме PHP, как настроить сервер, чтобы ущерб был минимальным, и что делать, если взлом уже случился.
Почему RCE в PHP — это не «экзотика»
PHP исполняется на сервере, а значит, любая ошибка в обработке входных данных потенциально затрагивает не только страницу, но и всю инфраструктуру: файлы, базу данных, переменные окружения, доступ к соседним сервисам. Язык гибкий: он умеет динамически подключать файлы, вычислять строки как код, десериализовать объекты и запускать системные команды. Именно эта гибкость в сочетании с невнимательностью разработчика и создаёт почву для RCE.
Вторая причина распространённости — масштаб экосистемы. Значительная часть сайтов в Рунете работает на WordPress, 1С-Битрикс, Joomla, Drupal, Laravel и других PHP-платформах. Каждая CMS подтягивает десятки плагинов, модулей и библиотек Composer, и уязвимость в любом из них превращается в точку входа для всего сайта.
Третья причина — «долгая жизнь» устаревших версий. Ветки PHP 7.4 и 8.0 больше не получают официальных исправлений безопасности, но на хостингах и в корпоративных проектах их по-прежнему можно встретить. Любая найденная в них уязвимость остаётся незакрытой навсегда.
Параметр запроса, cookie, заголовок или загружаемый файл
eval, include, unserialize или вызов системной команды без фильтрации
Сервер выполняет чужой код с правами процесса PHP
Веб-шелл, новые админ-аккаунты, задания в cron
Как возникает удалённое выполнение кода в PHP
В основе любой RCE-уязвимости лежит одна и та же схема: данные, которые контролирует пользователь, попадают в место, где они интерпретируются как код или команда. Достаточно, чтобы фильтрация отсутствовала или была реализована неполно.
Выделяют несколько типичных механизмов. Первый — динамическое вычисление кода: функция eval, а в PHP до 8.0 также assert со строковым аргументом, превращают строку в исполняемый код. Второй — запуск системных команд через system, exec, shell_exec, passthru, proc_open, popen и обратные кавычки. Третий — подключение файлов через include и require, когда путь частично или полностью формируется из параметров запроса. Четвёртый — небезопасная десериализация: unserialize на недоверенных данных позволяет создавать объекты и запускать цепочки магических методов (__wakeup, __destruct), которые в итоге приводят к выполнению кода. Пятый — загрузка файлов без проверки типа и каталога: вместо картинки на сервер попадает PHP-скрипт, доступный по прямой ссылке.
Отдельно стоит упомянуть уязвимости в самой среде выполнения. Они не зависят от качества вашего кода, но так же приводят к RCE. Здесь защита почти целиком сводится к обновлению и корректной конфигурации веб-сервера.
На практике реальный сайт редко страдает от одного «чистого» вектора. Обычно атака — это цепочка: уязвимый плагин позволяет загрузить файл, неправильные права на каталог дают возможность его исполнить, а отсутствие ограничений на уровне ОС позволяет злоумышленнику добраться до других сайтов на том же аккаунте хостинга.
| Вектор RCE | Типичная причина | Основная защита |
|---|---|---|
| Динамический код (eval, assert) | Пользовательская строка попадает в вычисляемое выражение | Отказаться от eval, использовать явную логику или шаблонизатор |
| Подключение файлов (include/require) | Путь к файлу берётся из параметра запроса без белого списка | Белый список допустимых значений, запрет allow_url_include |
| Системные команды (system, exec, shell_exec) | Данные формы подставляются в командную строку без экранирования | Использовать встроенные API вместо shell, escapeshellarg как крайняя мера |
| Десериализация (unserialize) | Объект восстанавливается из cookie или поля формы | Использовать JSON, при необходимости unserialize с allowed_classes=false |
| Загрузка файлов (upload) | Нет проверки содержимого и расширения, файл лежит в публичном каталоге | Хранить файлы вне webroot, запретить исполнение PHP в каталоге загрузок |
Реальные уязвимости из экосистемы PHP
Чтобы понять масштаб проблемы, полезно взглянуть на известные случаи. Они показывают, что RCE находят и в самом интерпретаторе, и в связках с веб-сервером, и в популярных фреймворках.
Уязвимость CVE-2024-4577 затронула PHP при работе в режиме CGI на Windows: из-за особенностей преобразования кодировки символов можно было передать интерпретатору дополнительные аргументы и выполнить код. Она была исправлена в версиях 8.1.29, 8.2.20 и 8.3.8. Уязвимость CVE-2019-11043 касалась связки Nginx и PHP-FPM при определённой конфигурации fastcgi_split_path_info и позволяла выполнять код через специально составленный адрес. В фреймворке Laravel ошибка CVE-2021-3129 в компоненте Ignition была опасна при включённом режиме отладки на рабочем сервере. А файл eval-stdin.php из старых версий PHPUnit (CVE-2017-9841), случайно оставленный в публичном каталоге vendor, годами использовался для массовых атак.
CVE-2024-4577: RCE в PHP-CGI на Windows без авторизации
Также 8.2.20 и 8.1.29 — обновление закрывает CVE-2024-4577
CVE-2021-3129 в Laravel опасна при включённом APP_DEBUG на боевом сервере
CVE-2017-9841: файл PHPUnit, доступный из браузера, даёт выполнение кода
Чем RCE опасна для бизнеса
- 01
Полный доступ к файлам сайта, базе данных и конфигурациям с паролями и API-ключами
- 02
Утечка персональных данных клиентов с рисками по закону № 152-ФЗ
- 03
Размещение скрытых ссылок, редиректов и спам-страниц, портящих репутацию домена
- 04
Использование сервера для майнинга, рассылки спама и атак на другие ресурсы
- 05
Пометка сайта как опасного в Яндексе и Google, падение трафика и позиций в выдаче
- 06
Простой сайта и затраты на расследование, восстановление и повторный запуск
Для интернет-магазина или сервиса с заявками последствия взлома ощущаются сразу: перестаёт работать форма заказа, из выдачи пропадают страницы, а браузеры показывают предупреждение. Даже после очистки сайта репутацию приходится восстанавливать неделями: поисковые системы повторно проверяют ресурс, а рекламные кабинеты, например Яндекс Директ и Google Ads, могут отклонять объявления на домен с историей заражения.
Поэтому экономика здесь простая: профилактика обходится в разы дешевле, чем ликвидация последствий. Ниже — разбор частой ошибки, с которой начинается большинство инцидентов.
Главная ошибка: доверять данным от пользователя
Как делать не нужно
- Подключать файл по имени из параметра, например по значению GET-переменной page
- Передавать в eval или shell_exec строку, собранную из полей формы
- Хранить сериализованные объекты в cookie и восстанавливать их через unserialize
- Проверять расширение загружаемого файла только на стороне браузера
- Оставлять включённым режим отладки и тестовые скрипты на боевом сервере
Как правильно
- 01
Использовать белый список разрешённых значений и сопоставлять их с заранее известными файлами
- 02
Заменить shell-вызовы встроенными функциями PHP или специализированными библиотеками
- 03
Хранить состояние в сессиях на сервере либо в JSON с подписью, а не в сериализованных объектах
- 04
Проверять MIME-тип и содержимое файла, переименовывать его и хранить вне webroot
- 05
Отключать отладку, удалять dev-зависимости командой composer install --no-dev
Защита на уровне сервера и конфигурации
Безопасный код — только половина решения. Вторая половина — настройка среды, которая ограничивает ущерб, если ошибка всё же проскочила. Принцип здесь называется «минимальные привилегии»: процесс PHP должен иметь ровно столько прав, сколько нужно для работы сайта.
Запретите исполнение PHP в каталогах загрузок (uploads, cache, tmp) на уровне Nginx или Apache. Настройте раздельные пользователи и пулы PHP-FPM для разных сайтов, чтобы взлом одного не затронул соседние. Используйте open_basedir, ограничивающий доступ к файловой системе, и отключите неиспользуемые опасные функции в disable_functions. Перед обновлением конфигурации проверьте, что этим функциям не доверяет ни CMS, ни ваши модули, иначе часть возможностей сайта перестанет работать.
Как выявлять уязвимости заранее
Системный подход состоит из нескольких регулярных практик. Во-первых, инвентаризация: вы должны знать версию PHP, CMS, каждого плагина и пакета Composer. Команда composer audit проверяет зависимости проекта по базе известных уязвимостей, а подписка на рассылки безопасности вашей CMS помогает узнавать об исправлениях в день выхода.
Во-вторых, статический анализ: инструменты вроде PHPStan и Psalm с правилами безопасности, а также поиск по коду вызовов eval, system, unserialize и include с переменными. В-третьих, мониторинг: следите за изменением файлов (контрольные суммы, git status на сервере), необычной нагрузкой и новыми заданиями cron. Также подключите сайт к Яндекс Вебмастеру и Google Search Console: оба сервиса уведомляют, если обнаружили вредоносный код или взлом, и показывают раздел с проблемами безопасности.
Наконец, периодически проводите тестирование на проникновение на копии сайта. Оно покажет те сценарии, которые не видны при чтении кода.
Что делать, если сайт взломали через RCE
- 01
Изолируйте проблему: переведите сайт в режим обслуживания или отключите его от сети, чтобы остановить дальнейший ущерб.
- 02
Сохраните доказательства: скопируйте файлы, дамп базы и логи веб-сервера и PHP до начала очистки.
- 03
Найдите точку входа: изучите access-логи на подозрительные запросы с длинными параметрами, обращения к неизвестным PHP-файлам, POST-запросы в каталоги загрузок.
- 04
Восстановите чистый код: разверните ядро CMS, плагины и проект из доверенных источников или репозитория, а не «лечите» заражённые файлы вручную.
- 05
Смените все секреты: пароли администраторов, FTP/SSH, доступ к базе данных, ключи API и соли CMS; удалите незнакомых пользователей и задания cron.
- 06
Закройте уязвимость: обновите PHP, CMS и зависимости, исправьте проблемный код и усильте конфигурацию сервера.
- 07
Проверьте репутацию: изучите разделы безопасности в Яндекс Вебмастере и Google Search Console, после очистки отправьте сайт на перепроверку.
После инцидента обязательно проведите разбор: что именно позволило атаке состояться, почему не сработал мониторинг и какие процессы нужно изменить. Без этого повторный взлом через несколько недель — очень вероятный сценарий: многие атакующие оставляют «чёрный ход» и возвращаются позднее.
Если вы работали с подрядчиком или хостингом, уточните, какие логи они хранят и за какой срок. Расследование без логов часто превращается в гадание, а закрытие найденной дыры не гарантирует, что других нет.
Итоги: как не допустить RCE в PHP-проекте
- 01
Не передавайте данные пользователя в eval, shell-функции, include и unserialize
- 02
Обновляйте PHP, CMS, плагины и пакеты Composer, не используйте версии без поддержки
- 03
Запретите исполнение PHP в каталогах загрузок и используйте раздельные пулы PHP-FPM
- 04
Отключите отладку и dev-зависимости на боевом сервере, настройте disable_functions и open_basedir
- 05
Подключите Яндекс Вебмастер и Google Search Console и храните логи для расследований
Безопасность — это процесс, а не разовое действие. Включите проверку зависимостей в рабочий цикл, регулярно просматривайте логи и тестируйте сайт на копии. Если вы хотите быть уверены, что сайт не содержит уязвимых мест, закажите независимый аудит: эксперт найдёт то, что незаметно при ежедневной работе.
Часто задаваемые вопросы
Это уязвимость, при которой злоумышленник через запрос к сайту заставляет сервер выполнить чужие PHP-код или системные команды. Права при этом равны правам пользователя, от имени которого работает PHP (например, www-data), чего обычно достаточно для кражи данных и размещения вредоносных файлов.
Это eval, assert (в старых версиях), system, exec, shell_exec, passthru, proc_open, popen, а также include и require с путём из пользовательских данных и unserialize для недоверенного ввода. Опасность создаёт не сама функция, а передача в неё непроверенных данных.
Начните с аудита версий PHP, CMS, плагинов и библиотек Composer по базам уязвимостей, затем проведите статический анализ кода и поиск опасных функций. Тестировать эксплуатацию на боевом сервере без согласования нельзя: используйте тестовую копию или привлекайте специалистов по пентесту.
Изолируйте сервер, сохраните логи и копию файлов для расследования, найдите точку входа, замените код чистой версией из репозитория, смените все пароли и ключи, закройте уязвимость и проверьте сайт в Яндекс Вебмастере и Google Search Console.
Нет. Это лишь дополнительный слой: он усложняет запуск системных команд, но не устраняет первопричину. Уязвимый код всё равно позволит выполнить PHP-код, читать файлы и работать с базой данных. Защита должна быть многоуровневой.
Проверьте свой PHP-сайт на уязвимости до того, как это сделают другие
Проведём аудит версий, кода и настроек сервера и пришлём список рисков с приоритетами
Спасибо за заявку!
Мы свяжемся с вами в ближайшее время.
Более 70% взломов начинаются с устаревшего ПО



