
Уязвимость PHP RCE: как работает и как защититься
RCE в PHP — это удалённое выполнение кода на сервере. Разбираем причины, известные CVE и способы закрыть уязвимость: обновления, настройки, код.
Уязвимость PHP RCE: что это и почему она опасна
RCE (Remote Code Execution) в PHP — это уязвимость, которая позволяет злоумышленнику выполнить произвольный код на вашем сервере через обычный запрос к сайту. Закрывается она сочетанием мер: своевременным обновлением PHP, CMS и библиотек, строгой фильтрацией пользовательского ввода и жёсткой настройкой сервера.
Из всех классов веб-уязвимостей RCE считается самым серьёзным. Если XSS крадёт сессию пользователя, а SQL-инъекция читает данные из базы, то RCE отдаёт атакующему управление самим сервером. Ниже разберём, как возникают такие уязвимости в PHP, какие известные CVE стоит знать и что сделать, чтобы ваш проект не попал в статистику взломов.
Что такое RCE простыми словами
Представьте, что у вашего сайта есть форма, где пользователь вводит адрес для проверки доступности. Скрипт на PHP берёт введённую строку и передаёт её в командную оболочку сервера. Если разработчик не проверил ввод, то вместо адреса можно подставить собственную команду, и сервер её выполнит. Это и есть удалённое выполнение кода: данные превратились в инструкции.
Код при этом исполняется с правами пользователя, от имени которого работает веб-сервер или PHP-FPM (часто это www-data или apache). Кажется, что права ограничены, но их хватает, чтобы прочитать конфигурационные файлы с паролями от базы данных, изменить файлы сайта, загрузить веб-шелл и использовать сервер как плацдарм для дальнейших атак.
RCE бывает двух видов. В первом случае ошибка находится в самом интерпретаторе или в связке PHP с веб-сервером. Во втором ошибка допущена в прикладном коде: в плагине, теме, самописном скрипте или сторонней библиотеке. Второй вид встречается намного чаще.
К чему приводит успешная RCE-атака
- 01
Кража данных: доступ к базе клиентов, заказам, логинам и хешам паролей, файлам .env и конфигурации
- 02
Установка веб-шелла: постоянного «чёрного хода», который остаётся даже после смены паролей
- 03
Внедрение вредоносного кода: скрытые редиректы, подмена контента, рассылка спама с вашего домена
- 04
Использование ресурсов сервера: скрытый майнинг, участие в DDoS-атаках, хостинг фишинговых страниц
- 05
Шифрование данных ради выкупа и полная остановка бизнеса
- 06
Потеря позиций и репутации: поисковые системы, включая Яндекс и Google, помечают заражённые сайты как опасные и могут понизить их в выдаче
Почему именно PHP так часто попадает под удар
PHP работает на огромной доле сайтов в интернете, в том числе на WordPress, Joomla, Drupal, Bitrix, Laravel и Symfony. Большая популярность делает язык привлекательной мишенью: достаточно найти одну ошибку, чтобы получить тысячи потенциальных жертв. Автоматические сканеры проверяют новые уязвимости практически сразу после публикации CVE.
Есть и причины в самом языке. Исторически PHP предоставлял много удобных, но потенциально опасных возможностей: динамическое подключение файлов, выполнение строки как кода, десериализация объектов, прямой вызов команд оболочки. Всё это легко использовать неправильно. Добавьте к этому множество устаревших версий на хостингах и неподдерживаемые плагины, и получите благодатную почву для атак.
Основные причины RCE в PHP
1. Инъекция команд ОС. Пользовательские данные попадают в функции system, exec, shell_exec, passthru, popen, proc_open или в оператор обратных кавычек. Без экранирования и белого списка допустимых значений ввод становится частью команды.
2. Выполнение кода из строки. Функция eval и ряд устаревших приёмов (например, модификатор /e в preg_replace, удалённый в PHP 7) превращают строку в код. Если часть строки приходит от пользователя, это прямая дорога к RCE.
3. Небезопасное подключение файлов (LFI/RFI). Конструкция include с параметром из запроса позволяет подключить чужой файл. Локальное включение часто комбинируют с записью в логи или загрузкой файла, чтобы добиться выполнения кода. Удалённое включение возможно при включённых allow_url_fopen и allow_url_include.
4. Небезопасная десериализация. Функция unserialize с недоверенными данными позволяет создавать объекты и запускать «магические» методы (__wakeup, __destruct). Через цепочки классов в популярных библиотеках получают выполнение кода. Отдельный вектор — архивы phar, которые десериализуются при обращении к файлам через обёртку phar://.
5. Небезопасная загрузка файлов. Если сайт принимает файл и сохраняет его в публичный каталог с исходным расширением, злоумышленник загружает PHP-скрипт и обращается к нему по URL. Проверка только заголовка Content-Type или расширения на стороне клиента не защищает.
6. Ошибки в интерпретаторе и связке с веб-сервером. Примеры: CVE-2012-1823, CVE-2019-11043 и CVE-2024-4577. Их устраняет только обновление PHP и корректная конфигурация.
7. Уязвимые зависимости, плагины и забытые файлы. Устаревшие компоненты, тестовые скрипты и инструменты разработки, оставленные на боевом сервере, нередко содержат готовые точки входа.
Ввод попадает в опасную функцию
Параметр запроса, cookie или загруженный файл без проверки передаются в eval, system, include или unserialize.
Код запускается на сервере
Команда исполняется с правами пользователя веб-сервера, например www-data.
Атакующий остаётся в системе
Появляются веб-шелл, задачи cron, новые ключи SSH и скрытые администраторы CMS.
Ущерб бизнесу
Утечка базы, редиректы на чужие ресурсы, майнер, рассылка спама, шифрование данных.
Известные случаи: чему они учат
История PHP знает немало громких RCE-уязвимостей. Их полезно изучить не ради любопытства: они показывают, что именно ломается чаще всего и как быстро после публикации CVE начинаются массовые сканирования.
В таблице ниже собраны несколько показательных примеров из разных категорий: ошибки интерпретатора, забытые инструменты разработки, уязвимости в CMS и фреймворках. Проверьте свои версии по этому списку, а затем сверяйтесь с официальными бюллетенями безопасности PHP и вашего фреймворка.
| CVE | Что затронуто | Суть проблемы | Как защититься |
|---|---|---|---|
| CVE-2012-1823 | PHP-CGI до версий 5.3.12 и 5.4.2 | Строка запроса без знака равенства передавалась интерпретатору как аргументы командной строки | Обновить PHP, использовать FPM вместо CGI |
| CVE-2017-9841 | PHPUnit до 4.8.28 и 5.6.3 (файл eval-stdin.php) | Доступный по HTTP скрипт выполнял PHP-код из тела запроса | Не ставить dev-зависимости на боевой сервер, закрыть каталог vendor от веба |
| CVE-2018-7600 | Drupal 7 и 8 (Drupalgeddon 2) | Недостаточная проверка данных форм позволяла выполнить код без авторизации | Обновить ядро Drupal до исправленной версии |
| CVE-2019-11043 | PHP-FPM с nginx и fastcgi_split_path_info; PHP до 7.1.33, 7.2.24 и 7.3.11 | Обработка путей с переводом строки приводила к повреждению памяти и выполнению кода | Обновить PHP и проверить конфигурацию nginx |
| CVE-2021-3129 | Laravel с Ignition до 2.5.2 при включённом режиме отладки | Страница отладки позволяла записать файл и запустить код через десериализацию phar | Обновить Ignition, отключить APP_DEBUG на продакшене |
| CVE-2024-4577 | PHP-CGI на Windows; версии до 8.1.29, 8.2.20 и 8.3.8 | Преобразование символов в кодировках Windows позволяло обойти защиту от CVE-2012-1823 | Обновить PHP, отказаться от CGI на Windows в пользу FastCGI |
Какие выводы можно сделать
Во-первых, уязвимости живут годами. CVE-2024-4577 по сути стала обходом защиты, созданной ещё в 2012 году. Во-вторых, самые простые ошибки, вроде забытого тестового файла или включённой отладки, дают тот же результат, что и сложные цепочки эксплуатации. В-третьих, время между публикацией уязвимости и началом массовых атак измеряется днями, поэтому откладывать обновления рискованно.
Теперь перейдём от общей картины к конкретному коду. Посмотрим, какие шаблоны в PHP чаще всего приводят к RCE и чем их заменить.
Типичные ошибки в коде, ведущие к RCE
Опасный подход
- system('ping ' . $_GET['host']) — строка пользователя попадает прямо в командную оболочку
- eval($_POST['formula']) — введённый текст исполняется как PHP-код
- include $_GET['page'] . '.php' — посетитель сам выбирает, какой файл подключить
- unserialize($_COOKIE['data']) — объекты создаются из недоверенного источника
- move_uploaded_file с исходным именем и расширением в публичный каталог — можно загрузить PHP-скрипт
Безопасная альтернатива
- 01
Не вызывать оболочку без необходимости: использовать встроенные функции или библиотеки; если вызов неизбежен, применять белый список значений и escapeshellarg
- 02
Вместо eval использовать разбор выражений специализированной библиотекой или фиксированный набор допустимых операций
- 03
Подключать файлы только из белого списка: сопоставлять ключ страницы с заранее известным путём
- 04
Использовать JSON вместо serialize; если unserialize необходим, передавать параметр allowed_classes со значением false или списком разрешённых классов, а данные подписывать
- 05
Генерировать имя файла самостоятельно, проверять тип по содержимому, хранить загрузки вне корня сайта или запрещать там исполнение PHP
Настройка окружения: второй рубеж обороны
Даже если в коде найдётся ошибка, правильная конфигурация сервера может не дать ей превратиться в полноценный захват системы. Это принцип эшелонированной защиты: каждый уровень снижает ущерб от провала предыдущего.
В PHP для этого есть несколько директив: disable_functions отключает опасные функции, open_basedir ограничивает каталоги, к которым имеет доступ скрипт, allow_url_include и allow_url_fopen управляют работой с удалёнными адресами, display_errors и expose_php скрывают лишнюю информацию. На уровне веб-сервера важно запретить исполнение PHP в каталогах загрузок и закрыть доступ к служебным каталогам и файлам вроде vendor, .git и .env.
- allow_url_include = On
- display_errors = On на боевом сервере
- expose_php = On
- Исполнение PHP разрешено в каталоге загрузок
- composer install с dev-зависимостями
- Старая версия PHP без обновлений безопасности
- allow_url_include = Off, allow_url_fopen по необходимости
- display_errors = Off, ошибки пишутся в журнал
- expose_php = Off
- Загрузки вне корня сайта или с запретом исполнения скриптов
- composer install --no-dev и проверка командой composer audit
- Поддерживаемая версия PHP с актуальными патчами
Настройки — не панацея
Важно понимать ограничения. Директива disable_functions не защищает от выполнения произвольного PHP-кода, она лишь затрудняет запуск системных команд, и существуют способы её обхода. Поэтому её нужно сочетать с обновлениями, исправлением кода и минимальными правами для пользователя, от имени которого работает PHP.
Отдельное внимание стоит уделить изоляции. Если на одном сервере живёт несколько сайтов, каждому нужен свой пользователь и свой пул PHP-FPM, чтобы взлом одного проекта не открывал доступ ко всем остальным. В контейнерах стоит запускать процесс не от root и делать файловую систему доступной только для чтения там, где это возможно.
План защиты PHP-проекта от RCE
- 01
Составьте инвентаризацию: версия PHP, режим работы (FPM, CGI, mod_php), CMS, фреймворк, плагины, темы и все зависимости Composer.
- 02
Обновите PHP до поддерживаемой ветки с актуальными исправлениями безопасности. Версии, снятые с поддержки, патчей не получают.
- 03
Обновите CMS, плагины и библиотеки; удалите неиспользуемые. Для проектов на Composer регулярно запускайте composer audit.
- 04
Уберите с боевого сервера всё лишнее: dev-зависимости, тестовые и установочные скрипты, резервные копии в веб-каталогах, файлы phpinfo.
- 05
Проведите ревью кода: найдите вызовы eval, system, exec, shell_exec, passthru, popen, unserialize и include с переменными, затем замените их безопасными аналогами.
- 06
Настройте php.ini и веб-сервер: disable_functions, open_basedir, запрет исполнения PHP в каталогах загрузок, закрытый доступ к служебным файлам.
- 07
Ограничьте права: отдельный пользователь для каждого сайта, запись только в нужные каталоги, доступ к базе данных с минимальными привилегиями.
- 08
Включите WAF и ограничение частоты запросов как дополнительный слой, а также резервное копирование с проверкой восстановления.
Мониторинг: как заметить проблему вовремя
Полностью исключить взлом невозможно, поэтому важно быстро его обнаружить. Настройте журналы веб-сервера и PHP, следите за изменениями файлов с помощью контроля целостности и регулярно сканируйте сайт антивирусом для серверов.
Используйте и инструменты поисковых систем. Яндекс Вебмастер и Google Search Console сообщают о проблемах безопасности, зараженном контенте и подозрительных страницах. Эти уведомления стоит включить сразу, а не искать их постфактум, когда трафик уже упал. Если подозрения подтвердились, не ограничивайтесь удалением найденного шелла: найдите точку входа, закройте её, смените все пароли и ключи и проверьте сервер на наличие других закладок.
Итоги: как не допустить RCE в PHP
- 01
Держите PHP, CMS, фреймворк и зависимости в актуальном состоянии
- 02
Никогда не передавайте пользовательский ввод в eval, оболочку, include и unserialize без строгой проверки
- 03
Уберите с боевого сервера dev-зависимости, тестовые скрипты и отладку
- 04
Настройте php.ini и веб-сервер по принципу минимальных привилегий
- 05
Следите за журналами и уведомлениями Яндекс Вебмастера и Google Search Console
Безопасность PHP-проекта — это процесс, а не разовая настройка. Регулярные обновления, ревью кода и мониторинг обходятся несоизмеримо дешевле, чем восстановление после взлома. Если нужна независимая оценка, начните с аудита версий и конфигурации: именно там чаще всего находят самые опасные и самые легко устранимые проблемы.
Часто задаваемые вопросы
RCE (Remote Code Execution) — это удалённое выполнение кода. Злоумышленник отправляет специально составленный запрос, и сервер выполняет его команды или PHP-код так, будто их написал администратор. Для этого не нужен доступ к панели управления или к серверу.
Нет. Обновление закрывает ошибки самого интерпретатора, например CVE-2024-4577 или CVE-2019-11043. Но большинство RCE возникает в прикладном коде, плагинах и сторонних библиотеках. Нужно обновлять CMS, фреймворк и зависимости, а также проверять свой код на небезопасные вызовы.
Типичные признаки: неизвестные PHP-файлы в каталогах загрузок и кэша, недавно изменённые файлы без вашего участия, странные задачи cron, скачки нагрузки на процессор, исходящие соединения на незнакомые адреса. Об угрозах может сообщить и Яндекс Вебмастер, и Google Search Console.
WAF (межсетевой экран веб-приложений) снижает риск и блокирует массовые автоматические атаки. Но он не заменяет исправление уязвимости: правила можно обойти. Относитесь к WAF как к дополнительному слою защиты, а не как к единственной мере.
Это eval, assert (в старых версиях), system, exec, shell_exec, passthru, popen, proc_open, а также небезопасные include и require с пользовательскими данными и unserialize для недоверенного ввода. Если они вам не нужны, их можно ограничить через директиву disable_functions.
Проверьте свой PHP-сайт на уязвимости до того, как это сделают другие
Мы проведём аудит кода и конфигурации сервера и подготовим понятный план исправлений
Спасибо за заявку!
Мы свяжемся с вами в ближайшее время.
RCE — самый критичный класс уязвимостей



