Перейти к содержанию

phpguard: защита непропатченного PHP от multipart-атак php2shell

Требуется план Pro (или выше) подписки GetPageSpeed NGINX Extras.

Установка

Вы можете установить этот модуль в любом дистрибутиве на базе RHEL, включая, но не ограничиваясь:

  • RedHat Enterprise Linux 7, 8, 9 и 10
  • CentOS 7, 8, 9
  • AlmaLinux 8, 9
  • Rocky Linux 8, 9
  • Amazon Linux 2 и Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install nginx-module-phpguard
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install nginx-module-phpguard

Включите модуль, добавив следующее в начало /etc/nginx/nginx.conf:

load_module modules/ngx_http_phpguard_module.so;

В этом документе описан nginx-module-phpguard v1.0.0, выпущенный 12 ноября 2026 г.


Защита непропатченного PHP от php2shell (CVE-2026-109618, CVE-2026-109619 и CVE-2026-115560) на уровне NGINX, без изменения PHP.

php2shell достигается через собственный парсер multipart/form-data в PHP. Этот парсер выполняется до любого кода приложения и до любой аутентификации, поэтому каждая точка входа PHP уязвима. Обычные правила NGINX могут только блокировать все multipart-запросы, что ломает загрузку файлов, и при этом они не учитывают кодировки, которые принимает PHP.

phpguard читает каждое multipart-тело в NGINX до того, как PHP увидит хотя бы один байт, и проверяет его с помощью потокового парсера, повторяющего поведение парсера PHP. Он был проверен на PHP 5.6, 7.4, 8.1, 8.4 и 8.5, за PHP-FPM и за Apache mod_php через proxy_pass.

Что блокируется

  • Поле прогресса загрузки сессии, подтверждённый триггер php2shell, во всех вариантах написания, которые принимает PHP: в кавычках или без, в любом регистре, со свёрнутыми заголовками, строками только с LF, составными типами содержимого, chunked-телами и HTTP/2.
  • Контрабандные части. Принимается только канонический multipart. Преамбула, эпилог, NUL или управляющие байты, неоднозначное экранирование кавычками или строка boundary где-либо, кроме настоящего разделителя, отклоняются. Это включает часть, которую PHP находит после 5120 байт, когда пропускает загрузку, которую не сохраняет.
  • Разборы с исчерпанием памяти. Память для разбора каждого запроса в PHP оценивается с помощью модели затрат, измеренной на каждой поддерживаемой версии PHP, и запрос отклоняется при превышении phpguard_max_memory. Пустые файловые поля с глубоко вложенными именами, например, иначе могут заставить PHP выделить более 100 МБ на несколько килобайт входных данных.

Проверяются только multipart-запросы. GET, JSON и URL-encoded формы стоят лишь одной проверки заголовка.

Конфигурация

Включите его для всего сервера, чтобы тело проверялось везде, где оно впервые читается:

server {
    phpguard block;

    location ~ \.php$ {
        fastcgi_pass unix:/run/php-fpm.sock;
        include fastcgi_params;
    }
}

Начните с phpguard detect;, чтобы логировать то, что было бы отклонено, ничего не блокируя.

Директивы

Директива По умолчанию Контекст Описание
phpguard off \| detect \| block off http, server, location block (псевдоним on) отклоняет; detect только логирует
phpguard_status <code> 403 http, server, location Статус для отказов по политике (400-599). Некорректные тела всегда получают 400
phpguard_progress_name <name> PHP_SESSION_UPLOAD_PROGRESS http, server, location Имя поля для отклонения; повторяйте для нескольких. Задайте его, если session.upload_progress.name изменён
phpguard_max_memory <size> 32m http, server, location Оценочная память разбора PHP на запрос. Держите её значительно ниже вашего наименьшего memory_limit
phpguard_max_parts <n> 1000 http, server, location Частей на тело
phpguard_max_name_length <n> 512 http, server, location Байт в имени поля
phpguard_max_nesting <n> 32 http, server, location Уровней [ в имени поля

Переменная $phpguard содержит причину отказа (пустая, когда запрос прошёл), например, в log_format.

Примечания

  • Индикаторы прогресса загрузки PHP (session.upload_progress) перестают работать, потому что их поле — это именно то, что отклоняется.
  • Multipart-тела полностью буферизуются до того, как PHP их получит. Это поведение NGINX по умолчанию; с fastcgi_request_buffering off загрузки перестают передаваться потоком в PHP.
  • Имя поля или файла, оканчивающееся обратной косой чертой, отклоняется. PHP всё равно неправильно разбирает такие имена.
  • phpguard покрывает multipart-пути php2shell, передаваемые в запросах. Он не исправляет PHP: ошибки исчерпания памяти, которых ваше приложение достигает через другие входные данные, и будущие ошибки PHP за пределами парсера multipart остаются. Обновляйте PHP, когда это возможно.