PHP est la surface d’attaque la plus exposée d’un hébergement WordPress. Par défaut, son comportement est orienté développement : il bavarde, il autorise, il expose. En production, l’objectif est inverse — réduire la surface d’attaque, taire les informations utiles à un attaquant, et limiter ce qu’un éventuel code malveillant peut faire sur le système.
Ce billet fait le tour des directives essentielles, regroupées par catégorie.
Prérequis : Ubuntu 24.04 LTS, nginx 1.24, PHP 8.3-FPM, MariaDB 10.11.
Les directives présentées ici s’appliquent dans /etc/php/8.3/fpm/php.ini sauf mention contraire.
1. Masquer les informations de version
expose_php = Off
Par défaut, PHP injecte sa version dans l’en-tête HTTP X-Powered-By :
X-Powered-By: PHP/8.3.x
Cette information aide un attaquant à cibler des CVE spécifiques à une version. expose_php = Off supprime cet en-tête.
Côté nginx,
server_tokens off;fait la même chose pour la version du serveur web. Les deux directives se complètent.
2. Gestion des erreurs : ne rien afficher, tout journaliser
display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /var/log/php8.3-fpm.log
display_errors = On est une catastrophe en production : les traces d’erreur PHP révèlent les chemins absolus, les noms de tables, parfois des fragments de configuration. Les désactiver à l’affichage ne signifie pas les ignorer — log_errors = On les redirige vers le fichier de log défini.
Vérifier que le fichier de log est accessible en écriture par www-data :
touch /var/log/php8.3-fpm.log
chown www-data:www-data /var/log/php8.3-fpm.log
chmod 640 /var/log/php8.3-fpm.log
Le niveau de verbosité est contrôlé par :
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
En production, on peut aussi restreindre davantage :
error_reporting = E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT
3. Désactivation des fonctions dangereuses
disable_functions = exec,passthru,shell_exec,system,proc_open,popen,pcntl_exec
Ces fonctions permettent d’exécuter des commandes système depuis PHP. Elles sont légitimes dans certains contextes (scripts CLI, outils d’admin), mais inutiles pour WordPress et franchement dangereuses si du code malveillant est injecté (shell web, plugin compromis).
| Fonction | Risque |
|---|---|
exec | Exécute une commande et retourne la dernière ligne |
passthru | Exécute et affiche la sortie brute |
shell_exec | Exécute via le shell, retourne la sortie complète |
system | Exécute et affiche en temps réel |
proc_open | Ouvre un processus avec contrôle des flux I/O |
popen | Ouvre un pipe vers un processus |
pcntl_exec | Remplace le processus courant par un exécutable |
Note WordPress : ces fonctions ne sont pas utilisées par le cœur de WordPress ni par les plugins courants (WooCommerce, Yoast, CF7…). En cas de doute sur un plugin, vérifier son code source avant activation.
4. Gestion des uploads
upload_max_filesize = 64M
post_max_size = 64M
Ces deux directives sont liées et doivent être cohérentes : post_max_size est la taille maximale du corps de la requête POST entière, qui contient le fichier uploadé. La règle est :
post_max_size >= upload_max_filesize
Pour un portfolio avec des images HD ou des PDF, 64M est un bon compromis. Ajuster selon les besoins réels.
Deux autres directives connexes à vérifier :
max_file_uploads = 20
max_input_time = 60
max_file_uploads limite le nombre de fichiers par requête. max_input_time (en secondes) limite le temps d’analyse des données entrantes — utile pour les connexions lentes.
5. OPcache : performances et sécurité
OPcache compile et met en cache le bytecode PHP. Le résultat : WordPress ne reparse pas ses quelques centaines de fichiers PHP à chaque requête.
Configuration recommandée
[opcache]
opcache.enable = 1
opcache.enable_cli = 0
opcache.memory_consumption = 128
opcache.interned_strings_buffer = 16
opcache.max_accelerated_files = 10000
opcache.revalidate_freq = 2
opcache.validate_timestamps = 1
opcache.save_comments = 1
opcache.fast_shutdown = 1
Explication des directives clés
| Directive | Valeur | Rôle |
|---|---|---|
opcache.enable | 1 | Active OPcache pour les requêtes web |
opcache.enable_cli | 0 | Désactive pour le CLI (évite des effets de bord avec WP-CLI) |
opcache.memory_consumption | 128 | Mémoire allouée en Mo pour le cache |
opcache.interned_strings_buffer | 16 | Mémoire pour les chaînes internées (noms de fonctions, variables) |
opcache.max_accelerated_files | 10000 | Nombre max de fichiers en cache (WordPress + plugins en génère beaucoup) |
opcache.revalidate_freq | 2 | Fréquence de vérification des modifications (en secondes) |
opcache.validate_timestamps | 1 | Vérifie si les fichiers ont changé — à mettre à 0 en prod stable pour le gain maximal |
opcache.save_comments | 1 | Nécessaire pour WordPress (annotations PHPDoc utilisées par certains plugins) |
opcache.fast_shutdown | 1 | Libération mémoire accélérée en fin de requête |
OPcache et sécurité : en plus du gain de performance, OPcache réduit indirectement la surface d’attaque en limitant les lectures disque à chaque requête. Un attaquant qui modifie un fichier PHP verra ses changements mis en cache jusqu’au prochain cycle de revalidation — ce qui peut être un avantage (lissage de la charge) ou un inconvénient (délai de détection). Avec
validate_timestamps = 1etrevalidate_freq = 2, le délai reste court.
Vérification du fonctionnement
php8.3 -r "var_dump(opcache_get_status());" 2>/dev/null | head -20
Ou via WP-CLI si installé :
wp eval 'var_dump(function_exists("opcache_get_status"));'
6. Autres directives utiles en production
Limites d’exécution
max_execution_time = 60
memory_limit = 256M
max_execution_time coupe les scripts qui tournent trop longtemps (protection contre les boucles infinies, les scripts mal écrits). memory_limit à 256M est généralement suffisant pour WordPress avec des plugins courants. Augmenter si WooCommerce ou des plugins lourds sont utilisés.
Sessions
session.cookie_httponly = 1
session.cookie_secure = 1
session.use_strict_mode = 1
session.sid_length = 48
WordPress gère ses propres cookies d’authentification, mais PHP peut être utilisé par des plugins. Ces directives durcissent le comportement des sessions :
httponly: le cookie n’est pas accessible via JavaScript (mitigation XSS)secure: transmission uniquement en HTTPSuse_strict_mode: rejette les identifiants de session non initialisés par le serveursid_length: augmente l’entropie des identifiants (défaut : 32)
Désactivation de allow_url_fopen et allow_url_include
allow_url_fopen = On
allow_url_include = Off
allow_url_include permet d’inclure des fichiers distants via include ou require — vecteur classique d’injection de code distant (RFI). Le désactiver est sans compromis.
allow_url_fopen autorise les fonctions de fichiers (file_get_contents, fopen…) à ouvrir des URLs distantes. WordPress en a besoin pour certaines fonctionnalités (mises à jour, vérification de licences) — le laisser à On est acceptable, mais à documenter.
7. PHP-FPM : pool et isolation des sites
La configuration php.ini s’applique globalement. Pour une isolation plus fine entre plusieurs WordPress, PHP-FPM permet de définir des pools distincts.
Fichiers concernés : /etc/php/8.3/fpm/pool.d/julien.conf et manon.conf.
Exemple minimal pour un pool dédié :
[julien]
user = www-julien
group = www-julien
listen = /run/php/php8.3-fpm-julien.sock
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 10
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
; Surcharge de directives php.ini pour ce pool uniquement
php_admin_value[open_basedir] = /var/www/julien.lagni.fr:/tmp:/usr/share/php
php_admin_value[error_log] = /var/log/php8.3-fpm-julien.log
php_admin_flag[log_errors] = on
open_basedir est particulièrement intéressant : il restreint PHP à une liste de répertoires, empêchant un WordPress compromis de lire les fichiers d’un autre site ou de /etc/passwd.
Attention :
open_basedirpeut casser certains plugins mal écrits qui font des appels en dehors de la racine web. Tester avec soin avant de déployer.
8. Application et vérification
Après modification de php.ini :
# Vérifier la syntaxe
php8.3 --ini
php8.3 -r "phpinfo();" | grep -E "expose_php|disable_functions|opcache.enable"
# Recharger PHP-FPM sans coupure
systemctl reload php8.3-fpm
# Vérifier que nginx passe toujours
nginx -t && systemctl reload nginx
Vérifier les directives actives depuis WordPress :
wp eval 'echo ini_get("expose_php") . "\n"; echo ini_get("disable_functions") . "\n";' --path=/var/www/julien.lagni.fr
Récapitulatif : /etc/php/8.3/fpm/php.ini — bloc de production
;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Sécurité - informations
;;;;;;;;;;;;;;;;;;;;;;;;;;;
expose_php = Off
;;;;;;;;;;;;;;;;;;;;;;;
;; Gestion des erreurs
;;;;;;;;;;;;;;;;;;;;;;;
display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /var/log/php8.3-fpm.log
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Fonctions dangereuses
;;;;;;;;;;;;;;;;;;;;;;;;;;
disable_functions = exec,passthru,shell_exec,system,proc_open,popen,pcntl_exec
;;;;;;;;;;
;; Uploads
;;;;;;;;;;
upload_max_filesize = 64M
post_max_size = 64M
max_file_uploads = 20
max_input_time = 60
;;;;;;;;;;;;;
;; Exécution
;;;;;;;;;;;;;
max_execution_time = 60
memory_limit = 256M
;;;;;;;;;;;;
;; Sessions
;;;;;;;;;;;;
session.cookie_httponly = 1
session.cookie_secure = 1
session.use_strict_mode = 1
session.sid_length = 48
;;;;;;;;;;;;;;;;;;;
;; Accès distants
;;;;;;;;;;;;;;;;;;;
allow_url_fopen = On
allow_url_include = Off
;;;;;;;;;;;
;; OPcache
;;;;;;;;;;;
opcache.enable = 1
opcache.enable_cli = 0
opcache.memory_consumption = 128
opcache.interned_strings_buffer = 16
opcache.max_accelerated_files = 10000
opcache.revalidate_freq = 2
opcache.validate_timestamps = 1
opcache.save_comments = 1
opcache.fast_shutdown = 1
Pour aller plus loin
- PHP : liste complète des directives de configuration
- WordPress : exigences serveur recommandées
- Prochain billet : AIDE — détecter les modifications non autorisées sur le système de fichiers
Laisser un commentaire