Introduction
En octobre 2026, deux vulnérabilités critiques du moteur PHP, surnommées « PHP2Shell » (CVE-2026-109618 et CVE-2026-109619), ont rappelé à de nombreuses équipes une réalité inconfortable : savoir quelle version de PHP tourne réellement, où, et avec quelle configuration, n'est pas toujours simple. Un serveur peut exécuter une version en ligne de commande, une autre derrière Nginx, une troisième dans un conteneur oublié.
Ce guide propose une méthode reproductible pour reprendre la maîtrise d'un serveur PHP : inventorier, mettre à jour sans casse, durcir la configuration, surveiller et réagir en cas de doute. Toutes les commandes présentées sont non destructives (lecture seule), sauf les redémarrages de service, signalés comme tels. Les exemples utilisent les chemins Debian/Ubuntu ; les équivalents Red Hat (RHEL, Rocky, Alma) sont indiqués quand ils diffèrent.
Pour le détail des failles PHP2Shell elles-mêmes, voir notre article PHP2Shell : comprendre les failles CVE-2026-109618 et CVE-2026-109619.
1. Inventorier les versions PHP en service
Pourquoi plusieurs versions coexistent
PHP s'exécute à travers différentes interfaces, appelées SAPI (Server API) :
- CLI : la commande
php, utilisée par les scripts, les tâches cron, Composer ; - PHP-FPM : un service autonome qui exécute PHP pour Nginx ou Apache ;
- mod_php : un module chargé directement dans Apache ;
- conteneurs : chaque image Docker embarque sa propre version de PHP.
Chaque SAPI peut avoir sa propre version et son propre php.ini. C'est la version utilisée par le serveur web qui détermine l'exposition aux failles réseau.
Version CLI
php -v
Affiche la version du binaire php présent dans le PATH. Utile, mais ce n'est que la version CLI.
ls /usr/bin/php* /usr/sbin/php-fpm* 2>/dev/null
Liste les binaires PHP installés. Plusieurs résultats (php8.2, php8.3…) signalent la coexistence de plusieurs versions.
update-alternatives --display php 2>/dev/null
Sur Debian/Ubuntu, montre vers quelle version pointe la commande php par défaut.
Version PHP-FPM
systemctl list-units --type=service | grep -i fpm
Liste les services PHP-FPM actifs (par exemple php8.3-fpm.service, ou php-fpm.service sur RHEL).
php-fpm8.3 -v # Debian/Ubuntu (adapter le numéro)
php-fpm -v # RHEL et dérivés
Affiche la version du binaire PHP-FPM.
Pour lever tout doute sur le binaire réellement en mémoire (après une mise à jour non suivie d'un redémarrage, par exemple) :
sudo ls -l /proc/$(pgrep -o php-fpm)/exe
pgrep -o php-fpm renvoie le PID du plus ancien processus PHP-FPM (le maître) ; le lien /proc/<pid>/exe indique le binaire exécuté. Si la sortie se termine par (deleted), le fichier a été remplacé sur disque par une mise à jour mais le processus tourne encore sur l'ancienne version : un redémarrage est nécessaire.
Apache et mod_php
apache2ctl -M 2>/dev/null | grep -i php # Debian/Ubuntu
httpd -M 2>/dev/null | grep -i php # RHEL
Liste les modules chargés par Apache. Un php_module signifie que PHP s'exécute dans Apache (mod_php) : c'est Apache qu'il faudra redémarrer après la mise à jour. Une absence de résultat indique en général qu'Apache délègue à PHP-FPM via proxy_fcgi.
dpkg -l | grep -E '^ii +(libapache2-mod-php|php[0-9.]+-(fpm|cli|cgi))' # Debian/Ubuntu
rpm -qa | grep -E '^php(-fpm|-cli|-common)?-[0-9]' # RHEL
Liste les paquets PHP installés et leur version exacte, telle que connue du gestionnaire de paquets.
Conteneurs
docker ps --format '{{.Names}}\t{{.Image}}'
Liste les conteneurs en cours avec leur image.
docker exec <nom_du_conteneur> php -v
Affiche la version de PHP dans un conteneur donné. Pensez aussi aux images utilisées par vos pipelines CI/CD et aux conteneurs arrêtés qui pourraient être relancés.
Version d'amont et version de distribution
Les distributions Debian, Ubuntu et Red Hat rétroportent les correctifs de sécurité sans changer le numéro de version amont. Un php -v qui affiche 8.3.6 sur Ubuntu 24.04 peut donc correspondre à un paquet parfaitement corrigé. Pour savoir si un correctif précis est installé, interrogez le journal du paquet :
apt changelog php8.3-fpm 2>/dev/null | grep -m3 CVE- # Debian/Ubuntu
rpm -q --changelog php-fpm | grep -m3 CVE- # RHEL
Affiche les dernières CVE mentionnées dans le journal des modifications du paquet. Pour PHP2Shell, cherchez CVE-2026-109618 et CVE-2026-109619.
Consigner l'inventaire
Notez, pour chaque serveur : système, SAPI, version, paquet, php.ini chargé, exposition (Internet ou interne), responsable. Ce tableau simple sera votre meilleur outil lors de la prochaine alerte.
2. Mettre à jour PHP sans casse
Avant l'intervention
- Lire le bulletin de la distribution ou les notes de version PHP : correctifs inclus, éventuels changements de comportement.
- Sauvegarder la configuration, le code et la base de données.
sudo tar czf /root/php-conf-$(date +%F).tar.gz /etc/php /etc/apache2 /etc/nginx 2>/dev/null
Crée une archive datée de la configuration PHP et des serveurs web (sur RHEL : /etc/php.ini /etc/php.d /etc/php-fpm.d /etc/httpd). Ne modifie rien sur le système.
- Vérifier ce qui va être mis à jour :
apt list --upgradable 2>/dev/null | grep -i php # Debian/Ubuntu
dnf check-update 'php*' # RHEL
Liste les paquets PHP pour lesquels une nouvelle version est disponible, sans rien installer. (Lancez sudo apt update au préalable pour rafraîchir la liste.)
- Tester sur un environnement de préproduction : même version de distribution, mêmes extensions, mêmes tâches planifiées.
Pendant l'intervention
- Installer les paquets via le gestionnaire habituel (
apt upgrade,dnf upgrade) ou reconstruire les images Docker à partir d'une image de base à jour (avecdocker build --pullpour forcer la récupération de la nouvelle base). - Redémarrer les services concernés :
sudo systemctl restart php8.3-fpmpour PHP-FPM,sudo systemctl restart apache2(ouhttpd) en cas de mod_php. Un rechargement (reload) de PHP-FPM relance les workers, mais un redémarrage complet garantit que le processus maître utilise le nouveau binaire.
Après l'intervention
- Revérifier la version du binaire en mémoire (
/proc/<pid>/exe, voir plus haut) : plus de mention(deleted). - Vérifier la configuration de PHP-FPM :
sudo php-fpm8.3 -tteste la syntaxe de la configuration sans démarrer de service. - Tester les parcours critiques de l'application : connexion, formulaire, paiement, envoi de fichier.
- Surveiller les journaux d'erreurs pendant quelques heures.
Les versions sans support
PHP 8.1 et antérieurs ne reçoivent plus de mises à jour du projet PHP, et PHP 8.2 arrive en fin de vie le 31 décembre 2026. Une version non maintenue accumulera des failles sans correctif. Planifiez la migration vers 8.3, 8.4 ou 8.5, en vérifiant la compatibilité de l'application et de ses dépendances.
3. Comprendre et maîtriser php.ini
Un fichier par SAPI
php --ini
Affiche le php.ini chargé par la CLI et les fichiers additionnels lus (conf.d).
Sur Debian/Ubuntu, chaque SAPI a son propre fichier :
| SAPI | Fichier principal | Fichiers additionnels |
|---|---|---|
| CLI | /etc/php/8.3/cli/php.ini |
/etc/php/8.3/cli/conf.d/ |
| PHP-FPM | /etc/php/8.3/fpm/php.ini |
/etc/php/8.3/fpm/conf.d/ + pools dans /etc/php/8.3/fpm/pool.d/ |
| mod_php | /etc/php/8.3/apache2/php.ini |
/etc/php/8.3/apache2/conf.d/ |
Sur RHEL, un fichier commun /etc/php.ini est complété par /etc/php.d/ et les pools /etc/php-fpm.d/.
Conséquence pratique : modifier /etc/php/8.3/cli/php.ini n'a aucun effet sur le site web. Et php -i affiche la configuration CLI, pas celle de PHP-FPM.
L'ordre de priorité
Une même directive peut être définie à plusieurs endroits. Du plus général au plus spécifique :
php.inide la SAPI et fichiersconf.d;- configuration du pool FPM (
php_value/php_admin_value) ou d'Apache (php_value/php_admin_valuedans unVirtualHost) ; - fichiers
.user.ini(PHP-FPM/CGI) ou.htaccess(mod_php) dans les répertoires du site ; ini_set()dans le code de l'application.
Les valeurs définies avec php_admin_value ou php_admin_flag ne peuvent pas être modifiées par les niveaux 3 et 4. Utilisez-les pour tout réglage de sécurité.
grep -rnE 'php_(admin_)?(value|flag)' /etc/php/*/fpm/pool.d/ /etc/apache2/ 2>/dev/null
Liste toutes les surcharges définies dans les pools FPM et la configuration Apache.
sudo find /var/www -name '.user.ini' -o -name '.htaccess' | head -50
Repère les fichiers de configuration par répertoire, qui peuvent redéfinir des valeurs en dehors des fichiers système.
Vérifier la configuration vue par le serveur web
La méthode la plus fiable consiste à afficher la configuration depuis le serveur web lui-même, avec un fichier temporaire protégé et supprimé immédiatement :
<?php
// Fichier temporaire : nom aléatoire, accès restreint, à supprimer après lecture
header('Content-Type: text/plain');
foreach (['memory_limit', 'post_max_size', 'upload_max_filesize',
'session.upload_progress.enabled', 'display_errors',
'expose_php', 'allow_url_include'] as $k) {
echo $k, ' = ', var_export(ini_get($k), true), PHP_EOL;
}
echo 'PHP ', PHP_VERSION, ' / SAPI ', PHP_SAPI, PHP_EOL;
Ce script n'affiche que quelques directives choisies, la version et le SAPI, contrairement à phpinfo() qui expose l'intégralité de l'environnement (variables, chemins, parfois des secrets). Restreignez son accès par IP et supprimez-le aussitôt.
Réglages de base recommandés en production
expose_php = Off ; ne pas annoncer la version dans les en-têtes HTTP
display_errors = Off ; ne jamais afficher les erreurs aux visiteurs
log_errors = On ; mais toujours les journaliser
error_reporting = E_ALL
allow_url_include = Off ; interdire l'inclusion de fichiers distants
session.cookie_httponly = 1
session.cookie_secure = 1 ; si le site est entièrement en HTTPS
session.use_strict_mode = 1
Des directives comme disable_functions ou open_basedir ajoutent des barrières utiles contre les failles applicatives classiques. Elles ne constituent pas une protection contre une corruption mémoire de l'interpréteur : elles complètent les mises à jour, sans les remplacer.
4. Permissions, comptes, secrets, téléversements et journaux
Comptes système
- Un pool PHP-FPM par site, chacun avec son propre compte système (
user/groupdans la configuration du pool). Une compromission reste ainsi cantonnée à un site. - Le compte qui exécute PHP ne doit jamais être
rootet ne doit pas disposer de droitssudo.
grep -rnE '^\s*(user|group|listen)\s*=' /etc/php/*/fpm/pool.d/
Affiche le compte et le socket d'écoute de chaque pool.
Permissions des fichiers
Principe : le code appartient à un compte de déploiement, le compte web n'a que la lecture, sauf sur les répertoires qui doivent réellement être inscriptibles (cache, téléversements).
sudo find /var/www -user www-data -name '*.php' | head
Liste les fichiers PHP appartenant au compte web. S'il y en a, ce compte peut modifier le code de l'application : une attaque réussie pourrait s'y installer durablement.
sudo find /var/www -perm -o+w -not -type l | head
Repère les fichiers et répertoires modifiables par n'importe quel utilisateur du système, une configuration à corriger.
Secrets
- Stocker les secrets (mots de passe de base, clés d'API) hors de la racine web, dans des variables d'environnement ou un fichier lisible uniquement par le compte du pool.
- Vérifier qu'aucun fichier sensible n'est téléchargeable :
.env,.git/, sauvegardes*.sql,*.bak.
curl -s -o /dev/null -w '%{http_code}\n' https://votre-site.fr/.env
Teste, sur votre propre site, si le fichier .env est accessible publiquement. Une réponse 200 est un problème à corriger immédiatement ; 403 ou 404 est attendu.
- Après tout incident, renouveler les secrets : un mot de passe qui a pu être lu doit être considéré comme compromis.
Téléversements
- Interdire l'exécution de PHP dans les répertoires de téléversement. Exemple Nginx :
location ^~ /uploads/ {
location ~ \.php$ { return 403; }
}
Toute requête vers un fichier .php placé dans /uploads/ est refusée, même si un attaquant parvient à en déposer un.
- Valider le type réel des fichiers (contenu, pas seulement l'extension), les renommer, limiter leur taille.
- Limiter la taille des requêtes au niveau du serveur web :
client_max_body_size 8m;(Nginx) ouLimitRequestBody 8388608(Apache). Ces limites frontales réduisent aussi l'exposition à certaines attaques sur l'analyse des requêtes, comme dans le cas de CVE-2026-109618.
Journaux
- Définir un
error_logexplicite pour chaque pool et vérifier qu'il est bien alimenté. - Conserver les journaux d'accès et d'erreurs suffisamment longtemps (plusieurs semaines au minimum) et, idéalement, les envoyer vers un système centralisé : un attaquant qui contrôle le serveur peut effacer les journaux locaux.
5. Surveiller les erreurs et les comportements suspects
Plantages de processus
Les corruptions mémoire se traduisent souvent par des plantages. Ces messages ne prouvent pas une attaque, mais une augmentation soudaine mérite une analyse.
sudo journalctl -u php8.3-fpm --since "2026-10-20" | grep -iE 'SIGSEGV|SIGBUS|exited on signal'
Recherche dans le journal système de PHP-FPM les workers terminés par un signal anormal (par exemple child 1234 exited on signal 11 (SIGSEGV)).
sudo grep -iE 'segmentation fault|exit signal' /var/log/apache2/error.log
Équivalent pour Apache (/var/log/httpd/error_log sur RHEL) : recherche les processus enfants terminés anormalement.
sudo grep -c 'Allowed memory size' /var/log/php*-fpm.log 2>/dev/null
Compte les erreurs de dépassement mémoire. Un pic inhabituel peut révéler des requêtes conçues pour épuiser la mémoire (adaptez le chemin au error_log configuré).
Requêtes inhabituelles
sudo awk '$6 ~ /POST/' /var/log/nginx/access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -20
Dans un journal Nginx au format par défaut, affiche les URL qui reçoivent le plus de requêtes POST. Des POST massifs vers des pages qui n'ont pas de formulaire méritent une attention particulière.
sudo awk '$9 >= 500' /var/log/nginx/access.log | tail -50
Affiche les dernières requêtes ayant provoqué une erreur serveur (codes 5xx), à corréler avec les plantages ci-dessus.
Comportements du compte web
ps -u www-data -o pid,ppid,etime,cmd
Liste les processus du compte web. Normalement, on n'y trouve que des workers PHP-FPM (ou Apache). Un shell (sh, bash), un outil réseau (curl, wget, nc) ou un binaire inconnu dans /tmp est un signal fort.
sudo lsof -u www-data -i -n -P
Affiche les connexions réseau ouvertes par le compte web. Hormis la base de données, le cache ou les API connues, une connexion sortante vers une adresse inconnue doit être analysée.
6. Réponse à incident : que faire en cas de suspicion
L'objectif est de préserver les preuves et de contenir l'incident, sans détruire les traces par précipitation.
Checklist de réponse à incident
1. Ne pas effacer
- Ne pas supprimer de fichiers suspects ni redémarrer le serveur avant d'avoir collecté les éléments : certaines traces n'existent qu'en mémoire.
- Noter l'heure de détection et chaque action réalisée.
2. Collecter (lecture seule)
sudo ps auxf > /root/ir-ps-$(date +%s).txt
sudo ss -tunap > /root/ir-ss-$(date +%s).txt
sudo crontab -u www-data -l > /root/ir-cron-www.txt 2>&1
Enregistre l'arborescence des processus, les connexions réseau et la crontab du compte web dans des fichiers horodatés. Copiez-les ensuite hors du serveur.
sudo find /var/www /tmp /var/tmp /dev/shm -type f -newermt "2026-10-20" -ls 2>/dev/null
Liste les fichiers créés ou modifiés depuis une date donnée dans les répertoires du site et les répertoires temporaires, souvent utilisés pour déposer des outils.
sudo find /var/www -path '*uploads*' -name '*.php*'
Recherche des fichiers PHP dans les répertoires de téléversement, où ils n'ont normalement rien à faire.
git -C /var/www/monsite status --short
Si le site est déployé depuis Git, affiche les fichiers modifiés ou ajoutés par rapport à la version suivie. Une méthode simple et fiable pour repérer un fichier injecté.
sudo dpkg --verify php8.3-fpm php8.3-common php8.3-cli # Debian/Ubuntu (adapter les paquets)
sudo rpm -V php-fpm php-common php-cli # RHEL
Compare les fichiers installés par les paquets PHP avec leurs empreintes d'origine et signale les fichiers modifiés (les modifications de fichiers de configuration sont normales).
3. Contenir
- Isoler le serveur (règles de pare-feu, retrait du répartiteur de charge) plutôt que l'éteindre.
- Faire une copie (snapshot) du disque si l'infrastructure le permet.
- Bloquer les accès sortants non indispensables.
4. Éradiquer et restaurer
- En cas de compromission avérée, reconstruire le serveur à partir d'une base saine plutôt que nettoyer : on n'est jamais certain d'avoir trouvé toutes les portes dérobées.
- Redéployer le code depuis le dépôt de référence, restaurer les données depuis une sauvegarde antérieure à l'incident.
- Appliquer les mises à jour avant la remise en ligne.
5. Renouveler et notifier
- Renouveler tous les secrets accessibles depuis le serveur : base de données, API, clés de session, comptes SMTP.
- Invalider les sessions utilisateurs.
- Évaluer s'il s'agit d'une violation de données personnelles : notification à la CNIL sous 72 heures si nécessaire, information des personnes concernées en cas de risque élevé.
- Faire appel à un prestataire spécialisé si l'ampleur le justifie (la plateforme cybermalveillance.gouv.fr référence des prestataires).
Checklist finale de sécurité pour un site PHP en production
Versions et mises à jour
- Inventaire à jour de chaque serveur : SAPI, version, paquet, exposition.
- Toutes les instances sur une branche PHP maintenue, avec les derniers correctifs.
- Abonnement aux annonces de sécurité de php.net et de la distribution.
- Redémarrage des services vérifié après chaque mise à jour (pas de binaire
(deleted)).
Configuration
- Réglages de sécurité définis avec
php_admin_value/php_admin_flag. -
display_errors = Off,log_errors = On,expose_php = Off,allow_url_include = Off. - Limites de taille de requête au niveau du serveur web et de PHP.
-
.user.iniet.htaccessaudités.
Comptes et fichiers
- Un pool et un compte système par site, jamais
root. - Code non modifiable par le compte web.
- Aucune exécution PHP dans les répertoires de téléversement.
- Secrets hors de la racine web ;
.env,.gitet sauvegardes non accessibles.
Surveillance et reprise
- Journaux d'erreurs et d'accès conservés et centralisés.
- Alertes sur les plantages de workers et les pics d'erreurs 5xx.
- Sauvegardes régulières, testées en restauration, stockées hors du serveur.
- Procédure de réponse à incident écrite et connue de l'équipe.
FAQ
Quelle est la différence entre le php.ini de la CLI et celui du serveur web ?
Chaque SAPI charge son propre fichier. Sur Debian/Ubuntu, /etc/php/8.3/cli/php.ini ne concerne que la ligne de commande ; le site web utilise /etc/php/8.3/fpm/php.ini (PHP-FPM) ou /etc/php/8.3/apache2/php.ini (mod_php). php -i n'affiche que la configuration CLI.
Faut-il redémarrer PHP-FPM après une mise à jour ? Oui. Les processus en cours continuent d'utiliser l'ancien binaire chargé en mémoire. Un redémarrage du service garantit que la version corrigée est active. Avec mod_php, c'est Apache qu'il faut redémarrer.
Mon hébergeur gère PHP. Ai-je quelque chose à faire ? Vérifiez la version proposée dans votre panneau d'administration et choisissez une branche maintenue. Pour les correctifs du binaire lui-même, consultez les communications de votre hébergeur. Les bonnes pratiques applicatives (permissions, secrets, téléversements) restent de votre responsabilité.
Les images Docker PHP sont-elles mises à jour automatiquement ?
Non. Un conteneur en cours d'exécution conserve la version de son image. Il faut reconstruire l'image à partir d'une base à jour (docker build --pull), puis recréer les conteneurs.
Comment savoir si mon serveur a été compromis ? Aucun signe isolé n'est une preuve. Les signaux les plus parlants : fichiers PHP inattendus (notamment dans les téléversements), processus ou connexions réseau inhabituels pour le compte web, tâches planifiées inconnues, modifications du code non expliquées par un déploiement. En cas de doute sérieux, suivez la checklist de réponse à incident.
disable_functions protège-t-il contre les failles de PHP lui-même ?
Non. Cette directive limite les fonctions utilisables par un script et complique certaines attaques applicatives, mais elle n'empêche pas l'exploitation d'une corruption mémoire de l'interpréteur. La protection passe par la mise à jour.
Conclusion
Sécuriser un serveur PHP repose moins sur des réglages exotiques que sur une discipline : savoir ce qui tourne, le maintenir à jour, limiter ce que chaque composant peut faire, et garder des traces exploitables. Concrètement :
- Dressez l'inventaire de vos SAPI et versions cette semaine, conteneurs compris.
- Industrialisez les mises à jour : préproduction, sauvegarde, redémarrage, vérification.
- Verrouillez la configuration avec
php_admin_valueet séparez les comptes par site. - Fermez les portes classiques : exécution PHP dans les téléversements, fichiers sensibles accessibles, code modifiable par le compte web.
- Préparez l'incident avant qu'il ne survienne : journaux centralisés, sauvegardes testées, procédure écrite.
Sources officielles
- Projet PHP — Avis de sécurité du 21 octobre 2026 : https://www.php.net/archive/2026.php#2026-10-21-5
- Projet PHP — Versions supportées : https://www.php.net/supported-versions.php
- Manuel PHP — Fichiers .user.ini : https://www.php.net/manual/fr/configuration.file.per-user.php
- Manuel PHP — Modifier la configuration (php_value, php_admin_value) : https://www.php.net/manual/fr/configuration.changes.php
- Manuel PHP — Configuration de PHP-FPM : https://www.php.net/manual/fr/install.fpm.configuration.php
- GitHub php-src — GHSA-w8hw-w9fx-j79w et GHSA-6wmf-4rv5-p3rp
- CNIL — Notifier une violation de données personnelles : https://www.cnil.fr/fr/notifier-une-violation-de-donnees-personnelles
- Cybermalveillance.gouv.fr : https://www.cybermalveillance.gouv.fr/