Voici un article technique détaillé sur la sécurisation de PHP dans un contexte multi-sites utilisant PHP-FPM avec Apache ou Nginx, en tenant compte des VirtualHosts, de la séparation des pools PHP-FPM, de l’accès aux fichiers système, et des fonctions sensibles de PHP.
🎯 Objectifs
-
Séparer les environnements PHP entre sites hébergés sur un même serveur.
-
Empêcher qu’un site PHP accède aux fichiers d’un autre.
-
Restreindre l’accès au système de fichiers.
-
Désactiver les fonctions PHP dangereuses (
exec,system, etc.). -
Éviter les élévations de privilèges via PHP-FPM.
🏗️ Architecture cible
-
OS : Debian/Ubuntu
-
Serveur Web : Apache ou Nginx
-
PHP-FPM avec un pool par site
-
Chaque site a son propre :
-
utilisateur système (ex:
site1,site2) -
pool PHP isolé
-
dossier
/var/www/siteX
-
1. 🧱 Séparation des utilisateurs système
🔧 Créer les utilisateurs Linux
sudo adduser --system --no-create-home --group site1
sudo adduser --system --no-create-home --group site2
🗂️ Propriétaire des fichiers web
sudo mkdir -p /var/www/site1
sudo mkdir -p /var/www/site2
sudo chown -R site1:site1 /var/www/site1
sudo chown -R site2:site2 /var/www/site2
2. ⚙️ Création de pools PHP-FPM séparés
Par défaut, PHP-FPM utilise un pool global (www.conf). On crée ici un pool par site.
🧾 Exemple : /etc/php/8.1/fpm/pool.d/site1.conf
[site1]
user = site1
group = site1
listen = /run/php/php8.1-fpm-site1.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
php_admin_value[open_basedir] = /var/www/site1:/tmp
php_admin_value[disable_functions] = exec,passthru,shell_exec,system,proc_open,popen,curl_exec
php_admin_value[session.save_path] = /tmp/site1
php_admin_flag[allow_url_fopen] = Off
php_admin_flag[display_errors] = Off
Reproduisez le même schéma pour
site2.conf,site3.conf, etc.
3. 🔐 Explication des directives de sécurité
-
open_basedir: limite PHP à certains chemins. Empêche un script PHP d’accéder au reste du système. -
disable_functions: bloque les fonctions dangereuses (shell, curl, etc.) -
allow_url_fopen = Off: évite l’inclusion de fichiers via URL (include("http://evil.com")) -
display_errors = Off: ne pas révéler les erreurs PHP en production
4. 🧩 Configuration Apache VirtualHost avec socket PHP
Exemple : /etc/apache2/sites-available/site1.conf
<VirtualHost *:80>
ServerName site1.example.com
DocumentRoot /var/www/site1
<Directory /var/www/site1>
Require all granted
Options -Indexes +FollowSymLinks
AllowOverride All
</Directory>
<FilesMatch .php$>
SetHandler "proxy:unix:/run/php/php8.1-fpm-site1.sock|fcgi://localhost/"
</FilesMatch>
ErrorLog ${APACHE_LOG_DIR}/site1_error.log
CustomLog ${APACHE_LOG_DIR}/site1_access.log combined
</VirtualHost>
Pour activer :
sudo a2ensite site1.conf
sudo systemctl reload apache2
5. 🧩 Configuration équivalente Nginx (si utilisé)
server {
listen 80;
server_name site1.example.com;
root /var/www/site1;
index index.php index.html;
location ~ .php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.1-fpm-site1.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
access_log /var/log/nginx/site1_access.log;
error_log /var/log/nginx/site1_error.log;
}
6. 🚧 Renforcement des permissions
🔒 Interdire l’accès à d’autres dossiers système
# Empêcher l’accès à /etc, /home, etc.
sudo chmod o-rx /etc /home
🔒 Sécuriser les sockets PHP-FPM
# Limiter l’accès uniquement à Apache/Nginx
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
7. 🔍 Bonus : chroot PHP-FPM (optionnel, avancé)
Il est possible de chrooter PHP-FPM pour isoler encore plus chaque site :
chroot = /var/www/site1
⚠️ Attention : cela nécessite de répliquer des binaires/librairies dans le chroot, ce qui est complexe à maintenir. Utilisez plutôt
open_basedir.
8. 🛡️ Autres bonnes pratiques de durcissement PHP
✂️ Limiter les extensions PHP installées
sudo apt remove php8.1-xdebug php8.1-sqlite3
🧯 Désactiver expose_php
Dans /etc/php/8.1/fpm/php.ini :
expose_php = Off
📦 Interdire les uploads si non utilisés
file_uploads = Off
9. 🔬 Vérification de l’isolation
🔁 Test inter-site
Depuis /var/www/site1/index.php :
<?php
echo file_get_contents("/var/www/site2/index.php");
→ Résultat attendu : interdit (open_basedir bloque l’accès)
🔁 Test de fonctions désactivées
<?php
echo exec("id");
→ Résultat : Warning: exec() has been disabled for security reasons
🧠 Résumé des points de contrôle
| Technique | Niveau de sécurité | Recommandé |
|---|---|---|
| Utilisateur système dédié par site. | 🟢 Élevé | ✅ Oui |
| open_basedir | 🟢 Élevé | ✅ Oui |
| disable_functions | 🟡 Moyen à élevé. | ✅ Oui |
| Pools PHP-FPM séparés | 🟢 Élevé | ✅ Oui |
| Chroot PHP | 🔴 Complexe | 🔶 Optionnel. |
| Permissions système | 🟢 Élevé | ✅ Oui |
✅ Conclusion
En isolant vos sites par :
-
utilisateur UNIX dédié,
-
pool PHP-FPM personnalisé,
-
VirtualHost configuré proprement,
-
fonctions PHP restreintes,
… vous améliorez considérablement la sécurité de votre serveur PHP dans un environnement multi-hébergé