Aller au contenu

phpguard : Protégez les PHP non corrigés contre les attaques multipart php2shell

Nécessite le plan Pro (ou supérieur) de l'abonnement GetPageSpeed NGINX Extras.

Installation

Vous pouvez installer ce module sur toute distribution basée sur RHEL, y compris, mais sans s'y limiter :

  • RedHat Enterprise Linux 7, 8, 9 et 10
  • CentOS 7, 8, 9
  • AlmaLinux 8, 9
  • Rocky Linux 8, 9
  • Amazon Linux 2 et 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

Activez le module en ajoutant ce qui suit au début de /etc/nginx/nginx.conf :

load_module modules/ngx_http_phpguard_module.so;

Ce document décrit nginx-module-phpguard v1.0.0 publié le 12 novembre 2026.


Protégez les PHP non corrigés contre php2shell (CVE-2026-109618, CVE-2026-109619 et CVE-2026-115560) au niveau de NGINX, sans toucher à PHP.

php2shell est atteint via le propre analyseur multipart/form-data de PHP. Cet analyseur s'exécute avant tout code applicatif et avant toute authentification, donc chaque point d'entrée PHP est exposé. De simples règles NGINX ne peuvent bloquer que toutes les requêtes multipart, ce qui casse les téléversements, et elles ratent les encodages que PHP accepte.

phpguard lit chaque corps multipart dans NGINX avant que PHP ne voie un seul octet, et le vérifie avec un analyseur en flux qui reproduit celui de PHP. Il a été vérifié sur PHP 5.6, 7.4, 8.1, 8.4 et 8.5, derrière PHP-FPM et derrière Apache mod_php via proxy_pass.

Ce qu'il bloque

  • Le champ de progression de téléversement de session, le déclencheur confirmé de php2shell, dans toutes les orthographes que PHP accepte : entre guillemets ou non, quelle que soit la casse, en-têtes repliés, lignes terminées par LF uniquement, types de contenu séparés par des virgules, corps chunked et HTTP/2.
  • Les parties dissimulées. Seul le multipart canonique est accepté. Un préambule, un épilogue, des octets NUL ou de contrôle, un guillemet ambigu, ou une chaîne de frontière partout sauf à un véritable délimiteur sont refusés. Cela inclut la partie que PHP trouve après 5120 octets lorsqu'il ignore un téléversement qu'il ne conserve pas.
  • Les analyses épuisant la mémoire. La mémoire d'analyse de chaque requête en PHP est estimée avec un modèle de coût mesuré sur chaque version de PHP prise en charge, et la requête est refusée au-delà de phpguard_max_memory. Des entrées de fichier vides avec des noms profondément imbriqués, par exemple, peuvent sinon faire allouer à PHP plus de 100 Mo pour quelques kilo-octets d'entrée.

Seules les requêtes multipart sont inspectées. Les formulaires GET, JSON et encodés en URL ne coûtent qu'une seule vérification d'en-tête.

Configuration

Activez-le pour tout le serveur, afin que le corps soit vérifié là où il est lu pour la première fois :

server {
    phpguard block;

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

Commencez par phpguard detect; pour journaliser ce qui serait refusé sans rien bloquer.

Directives

Directive Valeur par défaut Contexte Description
phpguard off \| detect \| block off http, server, location block (alias on) refuse ; detect se contente de journaliser
phpguard_status <code> 403 http, server, location Statut pour les refus de politique (400-599). Les corps malformés reçoivent toujours 400
phpguard_progress_name <name> PHP_SESSION_UPLOAD_PROGRESS http, server, location Nom de champ à refuser ; répétez pour en spécifier plusieurs. Définissez-le si session.upload_progress.name est modifié
phpguard_max_memory <size> 32m http, server, location Mémoire d'analyse PHP estimée par requête. Gardez-la bien en dessous de votre plus petite memory_limit
phpguard_max_parts <n> 1000 http, server, location Parties par corps
phpguard_max_name_length <n> 512 http, server, location Octets dans un nom de champ
phpguard_max_nesting <n> 32 http, server, location Niveaux de [ dans un nom de champ

La variable $phpguard contient la raison du refus (vide lorsque la requête est passée), par exemple dans un log_format.

Remarques

  • Les barres de progression de téléversement de PHP (session.upload_progress) cessent de fonctionner, car leur champ est exactement ce qui est refusé.
  • Les corps multipart sont entièrement mis en tampon avant que PHP ne les reçoive. C'est le comportement par défaut de NGINX ; avec fastcgi_request_buffering off, les téléversements cessent d'être transmis en flux à PHP.
  • Un nom de champ ou de fichier qui se termine par une barre oblique inverse est refusé. PHP analyse de toute façon mal de tels noms.
  • phpguard couvre les chemins multipart portés par les requêtes de php2shell. Il ne corrige pas PHP : les erreurs d'épuisement de mémoire que votre application atteint via d'autres entrées, et les futurs bogues PHP hors de l'analyseur multipart, subsistent. Mettez PHP à jour lorsque vous le pouvez.