À lire d'abord : cadre et limites de cet article Cet article est rédigé à des fins éducatives et défensives. Il n'explique pas comment réaliser une attaque et ne contient aucun code d'exploitation. Les faits sont ceux connus au 25 octobre 2026, soit quatre jours après la publication des correctifs : ils peuvent évoluer vite. Chaque affirmation importante renvoie à sa source ; lorsque la source est tierce ou non officielle, ou que je n'ai pas pu vérifier un point, je le dis.

Pourquoi PHP2Shell mérite notre attention

Le 21 octobre 2026, le projet PHP a publié un avis de sécurité qualifié de critique : deux vulnérabilités, CVE-2026-109618 et CVE-2026-109619, pouvant permettre à un attaquant non authentifié d'exécuter du code sur le serveur. L'ensemble est désigné « PHP2Shell » : c'est le nom donné à l'exploit par les chercheurs de Calif, qui ont découvert les failles avec OpenAI et Wiz, selon leur publication. Le projet PHP et Debian renvoient eux aussi à ce nom dans leurs notes.

Plutôt que de parler de séisme planétaire, restons factuels. Ce qui est établi : PHP lui-même juge que « toutes les installations PHP servant des requêtes web » sont concernées, avec un risque maximal pour Apache mod_php. Red Hat classe le sujet comme important et note que, sur les déploiements par défaut en PHP-FPM, l'exécution de code reste « difficile en pratique ». La société de sécurité Sansec estime (source tierce) qu'une exploitation de masse est moins probable qu'on le craignait, car les exploits publics qu'elle a observés ne fonctionnent pas sur une installation par défaut, tout en relayant des attaques précoces signalées par Cloudflare le jour de la publication. Le risque est donc réel, hétérogène selon la configuration, et le correctif est la seule solution complète.

Pourquoi l'exécution de commandes côté serveur est-elle si grave ? Parce qu'elle donne à l'attaquant les droits du processus web : lecture des fichiers de l'application et de ses secrets (mots de passe de base de données, clés d'API), modification du site, puis rebond vers d'autres systèmes du réseau interne. Ce qui sépare un incident gérable d'une compromission durable, c'est la rapidité de la correction et le cloisonnement des privilèges.

Partie 1 : PHP2Shell pour les nuls, comprendre le problème

Ce que désigne PHP2Shell

Ce sont deux défauts de gestion de la mémoire dans le moteur de PHP, pas un défaut d'une application en particulier. PHP n'est pas « intrinsèquement vulnérable » : ces bugs touchent le noyau du langage (le moteur qui exécute vos scripts) et ils sont corrigés dans des versions identifiées. D'après les avis officiels :

  • CVE-2026-109618 (avis GHSA) : PHP utilise un saut non local (longjmp()) pour sortir en urgence quand une allocation de mémoire échoue. Si cela arrive pendant que des données internes de la requête sont à moitié initialisées, le nettoyage de fin de requête lit un état invalide.
  • CVE-2026-109619 (avis GHSA) : un état interne appartenant à une requête peut survivre alors que la mémoire correspondante a été libérée. Une opération ultérieure du même processus peut alors l'utiliser (une « utilisation après libération », use after free) avant que l'initialisation normale ne le remplace.

Les deux ont reçu un score CVSS v4.0 de 9,2 d'après les sources secondaires consultées (je n'ai pas vérifié ce score auprès du CNA).

Le fonctionnement normal d'une requête

Un navigateur envoie une requête HTTP. Le serveur web (Apache, nginx...) la reçoit. Il la confie à PHP : soit en l'exécutant dans le même processus (mod_php, un module d'Apache), soit en la transmettant à un service séparé (PHP-FPM, via FastCGI). Avant même que votre code s'exécute, PHP analyse le corps de la requête (formulaires, fichiers téléversés) pour remplir $_POST et $_FILES. Une fois la réponse produite, PHP nettoie la mémoire de la requête et le processus passe à la suivante. C'est ce cycle « démarrage, analyse, exécution, nettoyage » que les failles perturbent.

Le mécanisme, sans le code

Les chercheurs de Calif décrivent la logique dans leur article :

  1. Provoquer une panne de mémoire contrôlée. PHP limite la mémoire par requête (memory_limit, 128 Mo par défaut). Une requête de type multipart/form-data (le format des envois de fichiers) peut, avec des champs imbriqués ou des parties vides, faire consommer beaucoup plus de mémoire interne que sa taille sur le réseau, tout en restant sous post_max_size. L'avis officiel ajoute les corps sans longueur déclarée utilisable et les parties à nombre d'en-têtes illimité.
  2. Interrompre PHP au mauvais moment. L'échec d'allocation fait sortir PHP en urgence au milieu d'une opération, sans exécuter le « rangement » prévu.
  3. Laisser un état incohérent. Selon l'avis officiel, avec mod_php, la phase d'arrêt de la requête est exécutée même si le démarrage a échoué, et elle traite donc cet état invalide. Tout cela se produit avant que le code de l'application ne s'exécute : un code applicatif irréprochable ne protège pas.
  4. Exploiter l'incohérence. Avec une préparation précise de la mémoire (heap), l'incohérence peut devenir une exécution de code.

Selon l'avis, PHP-FPM réagit mieux : son processus enfant se termine quand le démarrage échoue, ce qui évite de consommer l'état invalide. Mais l'analyse du code source laisse PHP-FPM « susceptible » via d'autres chemins, sans qu'un exploit complet pour ce mode ait été démontré. FrankenPHP est jugé probablement sensible comme mod_php, et LSAPI comme FPM (analyse de source, non validée par exploit).

Qui joue quel rôle ? Le code PHP (votre application) n'est pas en cause. Le moteur (Zend) contient le défaut. Les extensions peuvent aussi être impliquées : le chemin de CVE-2026-109619 le plus documenté passe par la fonction de suivi de progression d'envoi de l'extension session (session.upload_progress). L'intégration au serveur web (mod_php ou FPM) décide de la gravité. Le système d'exploitation n'est concerné qu'à la fin, comme victime : les commandes s'exécutent avec les droits de l'utilisateur du processus web.

Quant aux fonctions d'exécution de commandes comme system() ou exec(), rien dans les sources consultées ne fait de leur activation une condition de l'attaque : l'exécution de code vient de la corruption mémoire, pas d'un mauvais usage de ces fonctions. Les désactiver (disable_functions) reste une bonne pratique générale, mais je n'ai pas de source établissant qu'elle bloque cette attaque. Ne comptez pas dessus.

ClientServeur webPHPSystèmeRequête multipartpiégéeCorps transmis à PHPAnalyse du corps, mémoire saturéeSortie d'urgence, état incohérentCommandes possiblessi exploité
Chemin d'une requête malveillante : le défaut se déclenche pendant l'analyse du corps par PHP, avant le code de l'application ; l'exécution de commandes n'est qu'une conséquence possible.

Conséquences possibles

Cela dépend des privilèges et de la configuration : lecture de fichiers et de secrets accessibles à l'utilisateur web, altération de l'application, installation d'une porte dérobée, rebond vers d'autres machines. Calif indique avoir confirmé l'exécution de code avant authentification sur plusieurs applications connues (WordPress, Drupal, Joomla, Magento, Nextcloud, Laravel, etc., dans leurs versions récentes) : l'application n'est pas la faille, mais elle ne la protège pas.

Faits contestés ou nuancés Calif écrit que l'exploitation est « toujours possible avec les configurations par défaut » ; Sansec rapporte que les exploits publics testés ne fonctionnent pas sur une installation par défaut ; PHP désigne mod_php comme le cas démontré. Ces affirmations ne sont pas contradictoires (chemins et préconditions différents), mais retenez la prudence : une configuration par défaut n'est pas une protection.

Partie 2 : comment évaluer son exposition

1. Identifier la version et la provenance de PHP

php -v                      # version de la ligne de commande
php -i | grep -E "Server API|memory_limit|post_max_size|session.upload_progress.enabled"
rpm -q php-common           # RHEL, AlmaLinux, CentOS (paquet de la distribution ou de Remi)
dpkg -l 'php*' | grep ^ii   # Debian, Ubuntu

Attention : la ligne de commande n'utilise pas forcément le même php.ini que le serveur web. Pour connaître ce que voit réellement le service, interrogez-le dans son contexte (par exemple une page de diagnostic temporaire, protégée, puis supprimée) ou lisez la configuration du pool FPM et du vhost.

Notez aussi la provenance : paquet de la distribution, dépôt tiers, image Docker, binaires Windows, compilation locale. C'est elle qui détermine où chercher le correctif.

2. Identifier le mode d'exécution

apachectl -M | grep php (module Apache), systemctl status php*-fpm (FPM), présence de php-cgi, FrankenPHP ou LiteSpeed. Comme vu plus haut, mod_php est le cas le plus exposé selon PHP.

3. Comparer aux correctifs confirmés

BrancheStatut d'après php.netVersion corrigée officielle
8.5Maintenue8.5.12
8.4Maintenue8.4.27
8.3Maintenue8.3.36
8.2Maintenue8.2.35
8.1, 8.0, 7.4Fin de vieAucune version publiée ; branches de correctif minimal en source (voir partie 4)
7.3 et plus ancienFin de vieRien d'officiel

Les avis GHSA listent comme affectées toutes les versions antérieures à ces quatre numéros. Calif situe le chemin le plus ancien trouvé à PHP 5.4.0.

Piège classique : sur les paquets de distribution, le numéro de version ne change pas toujours. Un paquet corrigé peut afficher 8.1.34 tout en contenant le correctif. Vérifiez le journal des modifications du paquet (méthode donnée par Sansec, source tierce) :

# Debian, Ubuntu (adaptez la version)
zgrep CVE-2026-109618 /usr/share/doc/php8.2-common/changelog.Debian.gz
# RHEL, AlmaLinux, Rocky
rpm -q --changelog php-common | grep CVE-2026-109618

Même cette vérification a une limite : elle prouve qu'un mainteneur dit avoir traité la CVE, pas que le correctif est complet ni identique à celui de PHP (voir partie 4).

4. Scanner de détection ou outil de preuve d'exploitation ?

Un scanner de détection cherche des indices (version annoncée, comportement observable). Un outil de preuve d'exploitation prouve qu'on peut exécuter du code. Ne confondez pas les deux, et ne lancez ni l'un ni l'autre sur un système qui n'est pas le vôtre.

J'ai lu le code de php2shell-scanner, publié par l'équipe de recherche d'Assetnote (Searchlight), qui crédite Calif pour la découverte. Ce que le code fait réellement :

  • Il envoie une requête témoin puis jusqu'à trois requêtes multipart piégées (champs imbriqués sur 62 niveaux, de 1 à 20 Mio), soit environ 29 Mio au total par chemin testé.
  • Il conclut « vulnérable » sur une erreur de mémoire PHP, un HTTP 500 vide différentiel, un échec de worker PHP-FPM, ou une version annoncée connue comme non corrigée.
  • Il ne prouve pas l'exploitation : il observe un comportement.

Ses limites, reconnues dans son propre README :

  • Faux positifs : sur certaines configurations (memory_limit bas, max_multipart_body_parts élevé), une version corrigée renvoie la même réponse qu'une version vulnérable.
  • Faux négatifs : l'absence de plantage n'est pas une garantie. Un scan à distance ne voit ni la configuration interne ni les autres chemins (autres scripts, autres vhosts).
  • Versions rétroportées : d'après le code, une version annoncée inférieure à 8.2 sans correctif officiel est jugée vulnérable. Un paquet rétroporté (qui annonce encore 7.4.33) est donc classé « vulnérable » à tort si PHP expose sa version.
  • Impact réseau : c'est un test actif, qui peut tuer un worker ou saturer la mémoire. À réserver à vos systèmes, hors heures de pointe, avec accord interne.

Règle simple : un scan est un indice, pas un verdict. Une absence de résultat ne prouve rien ; un résultat positif sur une version rétroportée doit être vérifié autrement (journal du paquet, test de comportement en laboratoire).

Partie 3 : les solutions pour corriger le problème

Pour chaque stratégie : quand, prérequis, avantages et limites, contrôles après déploiement. Contrôles communs à toutes : redémarrer tous les workers (FPM, Apache), vérifier la version réellement chargée par le service, rejouer les tests applicatifs, surveiller les journaux d'erreurs.

PHP 8.2 à 8.5 (branches maintenues)

  • Quand : toujours, si l'application le permet.
  • Prérequis : paquet ou binaire en 8.2.35, 8.3.36, 8.4.27 ou 8.5.12 minimum.
  • Avantages : correctif officiel, supporté, sans dépendre d'un tiers.
  • Limites : un changement de branche (8.3 vers 8.4) est une migration, avec ses incompatibilités. Passer à PHP 8 ne corrige rien en soi : PHP 8.0 et 8.1 sont aussi concernés. Seules les versions 8.2.35, 8.3.36, 8.4.27 et 8.5.12 (ou plus récentes) contiennent le correctif officiel.
  • Contrôles : php -v côté service, test de chargement des extensions, tests de téléversement de fichiers.

Le correctif officiel remplace le saut longjmp() par une sortie plus propre : un arrêt « doux » au prochain point sûr de la machine virtuelle, et un arrêt immédiat si la consommation atteint un second seuil plus haut (avis GHSA). Il corrige donc le défaut, au lieu de le masquer.

PHP 7.x, 8.0 et 8.1 (branches en fin de vie)

D'après PHP, ces branches ne reçoivent plus de mises à jour du projet, mais l'équipe de sécurité a publié des correctifs minimaux en source pour 7.4, 8.0 et 8.1 : branches PHP-7.4-CVE-2026-109618-109619, PHP-8.0-... et PHP-8.1-.... Il s'agit de code source, sans archive ni binaire ni changement de numéro de version.

Pour 7.3 et plus ancien (5.x, 7.0 à 7.3), je n'ai trouvé aucun correctif officiel. Des tiers en proposent (voir partie 4), et leur équivalence avec le correctif officiel est à établir, pas à supposer.

  • Quand : migration impossible à court terme.
  • Prérequis : soit un fournisseur qui rétroporte, soit une compétence de compilation.
  • Limites : la disponibilité dépend du composant, du mainteneur et du mode de distribution ; aucun correctif n'est automatique.
  • Contrôles : voir partie 4.

Debian

D'après le suivi de sécurité Debian (consulté le 25 octobre 2026) : Debian 12 bookworm (php8.2) corrigé en 8.2.35-1~deb12u1 (DLA-4848-1) ; Debian 13 trixie (php8.4) corrigé en 8.4.27-1~deb13u1 (DSA-6574-1). Pour Debian 11 (php7.4), la seule piste relevée est le support étendu payant Freexian ELTS, d'après une recherche web que je n'ai pas revérifiée sur la page du fournisseur.

  • Quand : serveurs Debian avec les paquets de la distribution.
  • Prérequis : apt update && apt upgrade, dépôt security actif.
  • Avantages : correctif intégré, mises à jour automatisables.
  • Limites : les versions de PHP sont figées par la distribution ; le support d'une version ancienne peut être payant ou absent. Le dépôt tiers deb.sury.org (Ondřej Surý) propose des versions anciennes d'après Sansec : à évaluer comme tout dépôt tiers (partie 4).
  • Contrôles : apt list --installed 'php*', journal de modifications, redémarrage de FPM ou Apache.

CentOS et RHEL compatibles

Red Hat publie le bulletin RHSB-2026-013 (publié le 21 octobre 2026, mis à jour le 23). Il liste RHEL 7, 8, 9 et 10 comme concernés. Précision notable : sur le paquet PHP fourni avec RHEL 7, Red Hat indique que le code vulnérable pour CVE-2026-109618 n'est pas présent, et que seule CVE-2026-109619 s'applique. Cela ne vaut que pour le PHP de Red Hat : un PHP 7.4 venant d'un dépôt tiers (Remi, par exemple) est un autre logiciel, à traiter selon son propre fournisseur. D'après Sansec, Remi propose des correctifs pour 7.4, 8.0 et 8.1.

Le point de vigilance est la fin de vie des distributions : CentOS 7 n'est plus maintenu par le projet ; RHEL 7 n'est couvert que par un support étendu payant. Utiliser une distribution ancienne signifie souvent dépendre d'un tiers pour PHP. Sur AlmaLinux ou Rocky, vérifiez les errata de votre version.

  • Contrôles : rpm -q --changelog php-common | grep CVE-2026-109618, version et release du paquet, redémarrage des services.

Windows

PHP indique que les sources et les binaires Windows des versions corrigées sont sur la page de téléchargement, donc pour 8.2 à 8.5. Je n'ai trouvé aucun binaire Windows officiel corrigé pour 8.1 et plus ancien.

  • Procédure générale : identifier l'origine des binaires actuels (php.net, pile WAMP/XAMPP, installateur d'un éditeur), télécharger la version corrigée, vérifier la signature ou l'empreinte publiée, arrêter IIS ou Apache, remplacer le dossier PHP en gardant une copie de l'ancien et de php.ini, redémarrer.
  • Limites : les piles tout-en-un embarquent leur propre PHP, avec leur calendrier.
  • Contrôles : php -v et php --ini pour le bon dossier, page de diagnostic du service.

Docker

L'image officielle php est reconstruite pour les branches maintenues (d'après des sources tierces, dans les heures suivant la publication) et, d'après Sansec, aucun correctif n'est prévu pour 8.1 et plus ancien dans les images officielles.

  • Quand : applications conteneurisées.
  • Prérequis : tag récent (php:8.4-fpm en 8.4.27 ou plus), reconstruction de l'image (docker build --pull --no-cache), mise à jour des couches du dessous, redéploiement de tous les conteneurs, y compris ceux qui tournent depuis des mois.
  • Limites : une image « à jour » mais jamais redéployée reste vulnérable. Un tag mobile est pratique mais peu traçable : épinglez le condensat (digest) une fois validé.
  • Contrôles : docker run --rm <image> php -v, inventaire des images en production, recherche de conteneurs basés sur d'anciens tags.

Partie 4 : quand la montée de version est impossible

Cette partie s'adresse aux équipes qui maintiennent des applications anciennes (compatibilité, certification, coût, dépendances, contrats). La première décision est de savoir qui produit le correctif.

Origine du correctifCe que c'estCe qu'on peut vérifier
Mainteneur officielVersion publiée par le projet PHP (8.2.35 et plus)Signatures de publication, avis GHSA
Correctif source du projetBranches PHP-7.4, 8.0, 8.1 « CVE-2026-109618-109619 », sans version ni binaireDiff public, à compiler soi-même
Rétroportage (backport)Un tiers (distribution, Remi, éditeur LTS) adapte le correctif à une version ancienneJournal de modifications, patch publié, tests
Paquet recompilé par un tiersReconstruction à partir des sources d'un autre, avec ou sans modificationSources, patches, signature, reproductibilité
Paquet construit et signé par l'organisationPipeline interne (partie 5)Tout, en principe : à vous d'en assumer le coût

Étude d'un cas : le dépôt Rocketsky

Examinons objectivement les branches listées sur yum.rocketsky.ch. C'est une source tierce, non officielle, que j'évalue sans la présumer fiable ni dangereuse.

Ce que la page indique (consultée le 25 octobre 2026) : paquets RPM signés pour CentOS 7 x86_64, neuf branches marquées « Available » (5.6.40, 7.1.33, 7.4.33, 8.0.30, 8.1.34, 8.2.35, 8.3.36, 8.4.27, 8.5.12), construits sur le packaging EL7 de Remi, avec gpgcheck=1, repo_gpgcheck=1, sslverify=1, une empreinte de clé publiée, des RPM sources, des « provenance records », un index des paquets avec empreintes, et un plug-in qui empêche de changer de branche par accident. La page KNOWN-LIMITATIONS-7.4 décrit la release 20261021.1.el7.rocketsky : le micro-numéro reste 7.4.33, et le correctif adopté termine immédiatement le processus à l'épuisement de la mémoire (sans exécuter les fonctions d'arrêt) et désactive la progression d'envoi de session. Elle documente aussi des défauts existants (GD, ICU, CLI interactif).

Ce qui est vérifiable, donc à vérifier : l'empreinte de la clé par un autre canal que le site lui-même (le site le recommande), les RPM sources et le diff du correctif face au code officiel, le contenu exact de chaque paquet par rapport à celui que vous utilisez déjà.

Ce que je n'ai pas pu établir :

  • Dans les pages consultées, je n'ai pas trouvé d'identité d'éditeur ou d'organisation identifiable ; le site évoque un financement par dons (lien Ko-fi). Cela ne dit rien de la qualité du travail, mais cela pèse dans une décision d'entreprise (continuité, responsabilité, support).
  • Pour 5.6 et 7.1, PHP n'a publié aucun correctif source : le correctif est donc une adaptation par un tiers, dont je ne peux pas confirmer l'équivalence.
  • Le dépôt partagé rocketsky-php-common fournit des dépendances d'exécution ; la liste de ses RPM sources contient des composants système (par exemple httpd, glibc). Je n'ai pas déterminé s'ils peuvent remplacer des paquets système installés. À contrôler avant d'activer ce dépôt, par exemple en n'activant que le dépôt de la branche voulue avec --disablerepo='*' --enablerepo=... et en inspectant la transaction.
  • Aucune évaluation de la sécurité du reste de la chaîne de construction (machine de build, protection de la clé).

Mon appréciation : ni recommandation ni mise en garde. Un dépôt tiers peut être un pont raisonnable, à condition de le traiter comme une dépendance critique : vérifier, tester, prévoir la sortie.

Vérifications avant d'utiliser un dépôt tiers

  1. Identité et réputation du mainteneur : qui, quelle structure, depuis quand, quel historique d'avis publiés.
  2. Traçabilité des sources et des patches : sources amont identifiées, patches publiés et relisibles.
  3. Correspondance avec le correctif attendu : comparer le diff au correctif officiel de la branche source (par exemple les branches PHP-7.4/8.0/8.1) ; si le correctif est une variante (comme un arrêt immédiat au lieu d'un arrêt doux), comprendre les différences de comportement.
  4. Reproductibilité : peut-on reconstruire le RPM depuis le RPM source et obtenir un résultat comparable ?
  5. Signature et gestion des clés : empreinte vérifiée hors bande, gpgcheck et repo_gpgcheck actifs, plan en cas de rotation ou de compromission de la clé.
  6. Compatibilité : ABI (interface binaire), extensions (PECL, extensions maison), bibliothèques système, configuration existante.
  7. Tests de non-régression et retour arrière : un environnement de recette identique à la production, un paquet précédent conservé, une procédure de rollback testée.

Un numéro de version ne prouve rien

Un paquet « corrigé » peut garder le même numéro (7.4.33) : seule la release du paquet change. Inversement, un numéro identique peut cacher un paquet non corrigé. Validez donc techniquement : journal de modifications mentionnant la CVE, comparaison avec le correctif de référence, et test de comportement en laboratoire. Attention : le test doit correspondre au correctif. L'arrêt immédiat du processus et l'arrêt « doux » du correctif officiel ne se comportent pas de la même façon, et un test valable pour l'un peut ne pas l'être pour l'autre.

Partie 5 : construire son propre dépôt de paquets sécurisé

Pour les éditeurs, administrations et institutions qui doivent maîtriser la provenance de leurs paquets, un dépôt interne n'est pas un luxe. Distinguons trois choses souvent confondues :

  • Un miroir copie des paquets d'un tiers, sans rien changer. Il améliore la disponibilité, pas la confiance.
  • Un dépôt qui redistribue des paquets tiers les republie après un contrôle d'intégrité. Vous gagnez le contrôle de l'accès et du calendrier, mais le contenu reste celui du tiers.
  • Un pipeline interne de reconstruction et de validation part de sources vérifiées, applique des patches relus, compile, teste et signe avec votre clé. C'est la seule option qui vous donne la maîtrise de bout en bout.
1Source vérifiéearchive officielle2Intégritésignature,empreinte3Patch tracédiff relu4Build contrôléenvironnement isolé5Testsfonctionnels,sécurité6Signatureclé interneprotégée7Dépôt interneaccès authentifié8Déploiementprogressif, retourarrière
Chaîne d'approvisionnement maîtrisée : chaque étape produit une preuve (empreinte, diff, journal de build, résultats de tests) qui rend le paquet final explicable et reproductible.
  1. Récupérer une source identifiée. Archive officielle ou branche publique du projet, version précise notée.
  2. Vérifier intégrité et origine. Signature de publication, empreinte comparée par un second canal.
  3. Appliquer un patch traçable. Le correctif officiel quand il existe (pour 7.4, 8.0 et 8.1, les branches du projet PHP), relu et versionné.
  4. Compiler dans un environnement contrôlé. Machine ou conteneur de build propre, dépendances figées, journal conservé.
  5. Tester. Fonctionnel (vos applications), sécurité (comportement face aux requêtes piégées, en laboratoire), non-régression (téléversements, extensions).
  6. Signer les paquets. Clé dédiée, stockée à l'abri (module matériel ou coffre), avec procédure de rotation.
  7. Publier dans un dépôt authentifié. Accès restreint, métadonnées signées.
  8. Déployer progressivement. Quelques machines d'abord, surveillance, version précédente prête.

Cette approche est une stratégie de maîtrise des risques, pas une garantie. Elle a un coût réel : quelqu'un doit suivre les avis de sécurité de PHP et des bibliothèques liées (OpenSSL, libxml2, GD...), reconstruire régulièrement, renouveler les builds, maintenir la compétence. Une branche abandonnée par ses mainteneurs devient votre responsabilité : chaque nouvelle faille devra être portée par vous. Une fois la branche ancienne figée, le vrai objectif reste de la quitter.

Partie 6 : ajouter une défense complémentaire avec ModSecurity

Un pare-feu applicatif web (WAF) inspecte les requêtes HTTP avant qu'elles n'atteignent l'application. ModSecurity est un moteur de filtrage libre ; il s'installe comme module d'Apache, ou devant nginx via un connecteur. Il applique des règles, souvent regroupées dans un ensemble comme l'OWASP Core Rule Set (CRS), qui détecte des familles d'attaques génériques (injections SQL, XSS...).

1RequêteHTTPnavigateur, robot2WAFModSecurityinspecte, journalise, bloque3Serveur webApache, nginxreçoit si autorisé4PHPFPM ou mod_phpà corriger
Le WAF se place avant le serveur web : il inspecte, journalise et, une fois réglé, bloque. Il réduit l'exposition mais ne corrige pas PHP.

Détection, blocage, correction sont trois choses différentes. Détecter signale ; bloquer empêche la requête de passer ; corriger supprime le défaut. Seule la mise à jour de PHP corrige.

Pour PHP2Shell, les sources officielles recommandent d'agir aussi en frontal : borner la taille décodée des requêtes (y compris sans Content-Length), le nombre de champs, de parties multipart, d'en-têtes et la profondeur d'imbrication, ou bloquer le multipart si l'application n'en a pas besoin (avis GHSA). Des règles communautaires existent aussi (par exemple ce dépôt ou cette analyse) : ce sont des sources tierces que je n'ai pas auditées, et je n'ai pas trouvé d'ensemble de règles officiel de l'OWASP CRS dédié à ces CVE.

Limites d'un WAF : un WAF analyse le corps multipart à sa manière, et peut diverger de PHP (différences d'analyse, encodages, requêtes fragmentées) ; les variantes d'attaque contournent des signatures étroites ; des règles strictes provoquent des faux positifs (par exemple des formulaires légitimes à champs imbriqués) ; et tout chemin qui contourne le WAF (accès direct au serveur, autre port, service interne) reste exposé.

Un exemple à tester dans un environnement maîtrisé

Les directives et variables ci-dessous existent dans le manuel de référence de ModSecurity v2 (ARGS_NAMES, FILES_NAMES, opérateurs @streq, @rx). Je n'ai pas exécuté ces règles : la syntaxe suit la documentation, mais leur comportement sur votre pile reste à valider. Elles ne protègent pas de façon exhaustive.

# Observation uniquement : rien n'est bloqué
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecAuditEngine RelevantOnly

# Champ de suivi de progression d'envoi de session (voir CVE-2026-109619)
SecRule ARGS_NAMES "@streq PHP_SESSION_UPLOAD_PROGRESS" \
  "id:9900001,phase:2,pass,log,auditlog,msg:'Session upload progress field'"

# Noms de champs imbriqués sur trois niveaux ou plus (peut toucher des formulaires légitimes)
SecRule FILES_NAMES|ARGS_NAMES "@rx \[[^\]]*\]\[[^\]]*\]\[[^\]]*\]" \
  "id:9900002,phase:2,pass,log,auditlog,msg:'Deeply nested field name'"

Les identifiants 9900001 et 9900002 sont arbitraires : adaptez-les à votre plage locale.

Démarche opérationnelle

  1. Inventorier les points d'entrée : tous les scripts PHP joignables, vhosts, ports, chemins directs vers le serveur d'origine, accès internes.
  2. Activer et configurer : moteur de règles, accès au corps de requête, limites de taille, journal d'audit.
  3. Observer en mode détection (DetectionOnly) sur une période représentative.
  4. Analyser les journaux : quelles requêtes légitimes sont signalées, d'où viennent les autres.
  5. Ajuster : exclusions ciblées, seuils, niveau de paranoïa du CRS.
  6. Passer au blocage une fois les faux positifs traités, d'abord sur un périmètre restreint.
  7. Tester la non-régression : formulaires, téléversements, API.
  8. Surveiller en continu : alertes, revue périodique des règles. Un WAF jamais relu devient un faux sentiment de sécurité.

Le WAF complète la remédiation, il ne la remplace pas.

Partie 7 : plan d'action priorisé

Dans les 24 heures

  • Inventorier les serveurs PHP exposés à Internet, leur mode (mod_php, FPM), leur provenance et leur version réelle.
  • Appliquer les mesures temporaires et partielles de l'avis officiel, en comprenant leurs effets : memory_limit=-1 imposé de façon que les applications ne puissent pas le modifier (par exemple php_admin_value dans le pool FPM), post_max_size bas (8M, ou 32M si les envois de fichiers sont nécessaires), session.upload_progress.enabled=0 dans php.ini ou la configuration du pool (pas via ini_set() ni .user.ini), redémarrage de tous les workers. memory_limit=-1 retire le plafond de mémoire et augmente le risque d'épuisement : prévoyez une limite de conteneur ou de cgroupe, mais pas RLIMIT_AS ni RLIMIT_DATA, que l'avis déconseille car ils peuvent provoquer l'échec d'allocation visé. Sur PHP 8.5, fatal_error_backtraces=0 bloque le chemin connu par les retraces de fatales.
  • Appliquer les limites en frontal (taille décodée, nombre de parties).

Dans la semaine

  • Mettre à jour vers un correctif confirmé : 8.2.35, 8.3.36, 8.4.27, 8.5.12, ou paquet de distribution corrigé.
  • Pour les branches anciennes : choisir entre fournisseur LTS, dépôt tiers évalué (partie 4), reconstruction interne (partie 5).
  • Poser les contrôles compensatoires : WAF en détection, puis blocage.
  • Rechercher des signes de compromission : fichiers PHP récents inattendus, processus enfants anormaux du service web, requêtes multipart suspectes dans les journaux.

Dans le mois

  • Valider (tests de non-régression), surveiller, documenter la procédure de retour arrière.
  • Décider d'un éventuel dépôt interne et de qui le maintient.
  • Planifier la modernisation : une date de sortie de chaque branche en fin de vie.

Un troisième point à suivre : le 23 octobre, PHP a publié un avis informatif sur une troisième faille, CVE-2026-115560, liée à la même fonction de progression d'envoi de session ; selon Red Hat, la désactivation de cette fonction la neutralise entièrement. Je n'ai pas analysé cet avis en détail.

Synthèse des options

OptionPertinenceContraintesLimites
PHP 8.2 à 8.5 corrigéCas généralCompatibilité applicative, migrationUn numéro de branche récent ne suffit pas, il faut la bonne version
Paquet de distribution corrigéDebian, RHEL et dérivés encore supportésCycle de la distributionVersion figée, vérifier le journal du paquet
Fournisseur LTS payantBranches en fin de vie, besoin de supportCoût, dépendance contractuelleQualité et délais propres à chaque fournisseur
Dépôt tiers évalué (ex. Rocketsky)Pont temporaire sur CentOS 7Vérification de la clé, des patches, de l'ABIIdentité du mainteneur non établie, correctif d'origine non officielle pour 5.6 et 7.1
Compilation depuis les branches du projet7.4, 8.0, 8.1 avec compétence internePipeline, testsResponsabilité de chaque correctif futur
Dépôt et pipeline internesÉditeurs, administrationsCoût de maintenance, gestion de clésPas une garantie ; renouvellement continu
Réglages PHP et limites en frontalUrgenceEffets de bord (mémoire, envois de fichiers)Partiel : n'élimine pas le défaut
WAF (ModSecurity)Défense en profondeurRéglage, faux positifsContournable, ne remplace pas le correctif

Sources et références

Sources primaires (projet PHP et éditeurs) :

Sources tierces ou non officielles :

Points non vérifiés par moi : le score CVSS (relevé dans des sources secondaires), l'état du support Debian 11 (ELTS), les correctifs de Remi et de Sury (cités par Sansec), l'état des images Docker officielles pour 8.1 et antérieures (Sansec), l'équivalence des correctifs tiers avec ceux du projet, ainsi que toute évolution postérieure au 25 octobre 2026.