Situation actuelle : PHP2Shell. Deux failles PHP critiques (CVSS 9.2) permettent d'exécuter du code malveillant sans authentification. Correctifs disponibles depuis le 21 octobre 2026.Ce qu'il faut faire maintenant →

Isoler les pools PHP-FPM site par site

Un pool dédié par site est un début, pas une fin. Voici comment vérifier les pools existants et durcir sockets, extensions, pages de statut, ressources et journaux.

État 6 min de lectureDurcissement et configuration

En bref : chaque site d'un serveur devrait avoir son propre pool PHP-FPM, avec son propre utilisateur système et son propre socket. La séparation ne devient toutefois efficace que grâce aux détails : droits du socket réservés au serveur web, security.limit_extensions limité à .php, aucune page de statut publiquement accessible, limites propres pour les processus et la durée d'exécution, ainsi que des journaux séparés. Contre PHP2Shell lui-même, seule la mise à jour protège ; les pools limitent la portée d'une attaque.

Un serveur, plusieurs sites, un risque commun

Sur de nombreux serveurs, ce que personne n'avait prévu ainsi s'est développé au fil des années. Le site de l'entreprise, la boutique, une page de campagne de 2019 et un système de test tournent côte à côte, souvent sous le même utilisateur et dans le même pool. Si la page de campagne oubliée est compromise, les données clients de la boutique sont elles aussi exposées.

PHP-FPM, le gestionnaire de processus FastCGI de PHP, résout cela avec des pools : des groupes de processus workers aux paramètres propres. Le principe de base est expliqué dans PHP-FPM expliqué : gestionnaire de processus, pools et workers. La mise en place de pools séparés avec utilisateur dédié, dossier temporaire dédié et open_basedir est décrite dans le guide open_basedir et isolation par site. Cet article va un pas plus loin : comment vérifier les pools existants, et quels paramètres rendent la séparation solide ?

Point important pour la mise en perspective : avec PHP2Shell, les failles CVE-2026-109618 et CVE-2026-109619 se trouvent dans l'interpréteur PHP, que partagent tous les pools d'une version de PHP. Un pool dédié n'empêche pas l'exploitation. Mais il détermine si un attaquant atteint ensuite un site ou tous.

Pools PHP-FPM avec utilisateur dédié : vérifier l'existant

Avant de modifier quoi que ce soit, faites le point. Les commandes suivantes ne font que lire. Les chemins et numéros de version varient selon la distribution :

# Tous les pools avec utilisateur et socket
grep -HE '^\s*(\[|user|group|listen)\s*' /etc/php/*/fpm/pool.d/*.conf

# Pools en cours d'exécution et leurs utilisateurs
ps -eo user,pid,cmd | grep '[p]hp-fpm: pool'

# Sockets et leurs droits
ls -l /run/php/

# Vérifier la configuration (ne modifie rien)
sudo php-fpm8.3 -t

Soyez attentif à trois cas de figure : plusieurs sites dans le même pool, des pools qui tournent sous www-data ou même sous root, et des sockets accessibles en écriture à tous les utilisateurs. Chacun de ces cas sape la séparation.

Si plusieurs versions de PHP tournent sur le serveur, chacune a son propre service FPM avec ses propres répertoires de pools. Vérifiez-les toutes, pas seulement la version que vous avez configurée en dernier. Les anciennes versions avec des pools oubliés sont un risque, car elles ne sont souvent ni entretenues ni redémarrées après les mises à jour. Des indications se trouvent dans Plusieurs versions de PHP sur un serveur. Consignez le résultat dans un petit tableau : site, pool, utilisateur, socket, version de PHP. Cet aperçu est aussi la base de toute modification ultérieure.

Les paramètres les plus importants par pool

L'aperçu suivant s'appuie sur la configuration FPM du manuel PHP. Les valeurs pour les processus et les durées dépendent de votre charge et de votre application :

Paramètre Objectif Recommandation
user, group utilisateur des workers un utilisateur dédié par site, jamais root
listen.owner, listen.group, listen.mode droits sur le socket uniquement le groupe du serveur web, mode 0660
listen.acl_users accès par ACL au lieu du propriétaire n'inscrire que l'utilisateur du serveur web
security.limit_extensions extensions exécutables limiter à .php (par défaut .php .phar)
clear_env vider les variables d'environnement laisser à la valeur par défaut yes
pm.max_children nombre maximal de workers limiter pour qu'un site ne sature pas le serveur
request_terminate_timeout interruption des requêtes bloquées régler selon la requête légitime la plus longue
pm.status_path, ping.path page de statut et de ping ne définir que si nécessaire, et sécuriser en interne
slowlog, request_slowlog_timeout journal des requêtes lentes un fichier propre par pool

Un extrait pour un pool durci ressemble à ceci :

; /etc/php/8.3/fpm/pool.d/shop.conf (extrait)
[shop]
user = shop
group = shop
listen = /run/php/shop.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

pm = dynamic
pm.max_children = 20
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 6
request_terminate_timeout = 60s

security.limit_extensions = .php
clear_env = yes

slowlog = /var/log/php/shop.slow.log
request_slowlog_timeout = 10s
php_admin_value[error_log] = /var/log/php/shop.error.log

Des paramètres comme memory_limit ou session.upload_progress.enabled se définissent par pool avec php_admin_value et php_admin_flag. Selon le manuel, ces valeurs ne peuvent pas être écrasées par ini_set(). Plus de détails dans php_admin_value : verrouiller les paramètres contre l'écrasement.

Bien situer les pages de statut et le renouvellement des workers

pm.status_path et ping.path ne sont actifs que s'ils sont définis. La page de statut montre les requêtes en cours, les processus et la charge. C'est utile pour la supervision, mais, accessible publiquement, c'est une source d'information inutile pour les attaquants. Ne transmettez ces chemins dans le serveur web que pour des adresses internes. La manière dont Nginx et PHP-FPM coopèrent en toute sécurité est décrite dans Sécuriser Nginx avec PHP-FPM.

pm.max_requests détermine après combien de requêtes un worker redémarre ; la valeur par défaut est 0, c'est-à-dire jamais. Une valeur supérieure à zéro aide contre une consommation de mémoire qui croît lentement. Il n'est pas conçu comme protection contre PHP2Shell : php.net ne le cite pas parmi les mesures transitoires et souligne que seule la mise à jour protège complètement. Après la mise à jour, il faut en outre redémarrer tous les workers de tous les pools, car sinon les anciens processus continuent de tourner avec le code vulnérable.

Astuce

Tenez à jour un modèle pour les nouveaux pools et créez-y chaque nouveau site. La séparation se fait ainsi automatiquement, au lieu d'être rajoutée des années plus tard.

Erreurs typiques lors du changement

L'erreur la plus fréquente est un nouveau pool qui tourne bien sous un utilisateur dédié, mais dont les fichiers appartiennent toujours à www-data. Le pool ne peut alors pas écrire là où il le doit, et quelqu'un définit des droits trop larges. Clarifiez d'abord quels dossiers doivent être accessibles en écriture. La deuxième erreur concerne les tâches planifiées qui continuent de tourner sous l'ancien utilisateur et créent des fichiers avec un mauvais propriétaire. La troisième est un dossier de sessions commun à tous les pools, qui annule à nouveau la séparation.

Après chaque changement, testez la connexion, les uploads, les caches et les tâches planifiées du site, et surveillez le journal d'erreurs du pool.

Comment PHP SOS aide-t-il concrètement ?

  • Nous recensons tous les pools, utilisateurs, sockets et paramètres, et montrons où des sites partagent aujourd'hui des processus ou des droits.
  • Nous mettons en place pour chaque site un pool durci, avec droits de socket, limite d'extensions, limites de ressources et journaux propres.
  • Nous sécurisons les pages de statut, redémarrons tous les workers de façon contrôlée et testons chaque site.
  • Dans le cadre du durcissement de serveur, nous documentons la séparation comme modèle pour les projets futurs.

Questions fréquentes

Pourquoi chaque site devrait-il avoir son propre pool PHP-FPM ?

Un pool dédié avec son propre utilisateur système sépare les sites au niveau des processus et des droits sur les fichiers. Si un site est compromis, le code ne peut pas lire sans difficulté les fichiers des autres. De plus, limites et paramètres peuvent être définis séparément pour chaque site.

Un pool dédié protège-t-il contre PHP2Shell ?

Non. Les failles se trouvent dans l'interpréteur PHP lui-même, que tous les pools partagent. Seule la mise à jour vers une version corrigée, avec redémarrage de tous les workers, protège. Mais des pools séparés limitent la portée d'une attaque réussie.

Que fait security.limit_extensions ?

Selon le manuel PHP, ce paramètre limite les extensions de fichiers que PHP-FPM exécute comme script principal. Par défaut, ce sont .php et .phar. Les limiter à .php empêche par exemple que des fichiers image téléversés soient exécutés comme PHP si le serveur web est mal configuré.

La page de statut de PHP-FPM est-elle dangereuse ?

Elle révèle des détails sur les processus et les requêtes en cours. Si elle est publiquement accessible sans protection, elle facilite l'orientation des attaquants. Selon le manuel, elle n'est active que si pm.status_path est défini. Le serveur web devrait alors restreindre ce chemin aux adresses internes.

Sources

Contact immédiat

Chaque heure compte. Écrivez-nous maintenant.

Décrivez brièvement votre système, votre version de PHP et ce que vous avez observé. Nous vous répondons, faisons le point et travaillons à distance jusqu'à la clôture et la documentation de l'incident.

Aide immédiate