Aller au contenu principal
Terrain

Durcissement WordPress : configuration serveur et .htaccess

13 octobre 20265 min de lecturePar Slim Sayari

Durcissement WordPress : configuration serveur et .htaccess

Un plugin de sécurité WordPress s'exécute après que PHP a pris en charge la requête. Si xmlrpc.php est accessible, si PHP tourne dans le dossier uploads ou si la version du serveur apparaît dans les en-têtes HTTP, ces expositions ne se corrigent pas depuis l'admin WordPress : elles se traitent au niveau du serveur web. Ce guide vous donne les directives Nginx, Apache et wp-config.php à appliquer, avec un template Nginx complet et commenté directive par directive.

Les configurations présentées s'appliquent sur un VPS ou serveur dédié Linux avec accès SSH. Sauvegardez la configuration courante du serveur web et testez chaque bloc sur un environnement de staging avant de l'appliquer en production.

Pourquoi la configuration serveur prime sur les plugins

Les vecteurs que les plugins ne couvrent pas

Un plugin de sécurité s'initialise en milieu de cycle WordPress, après le chargement de PHP et de plusieurs centaines de lignes du core. À ce stade, xmlrpc.php a déjà été invoqué, le processus est en mémoire. Une attaque qui exploite ce fichier n'attend pas que le plugin de sécurité réagisse.

Les vecteurs traités uniquement au niveau serveur :

  • Fichiers exposés : xmlrpc.php, readme.html, license.txt, fichiers de sauvegarde (.sql, .bak) oubliés dans le répertoire web.
  • Informations de version : les en-têtes HTTP Server (Apache/2.4.x ou nginx/1.24.x) et X-Powered-By (PHP/8.x) indiquent à un attaquant quelles CVE cibler.
  • Exécution PHP dans uploads : sans restriction serveur, un fichier PHP déguisé en image peut être exécuté directement via son URL.

Le guide audit et durcissement WordPress pour agences décrit la méthode complète d'évaluation du parc. Pour une vérification sans installation, le scan gratuit Sentinelo contrôle 19 points de sécurité WordPress en quelques secondes.

Directives Nginx essentielles

Masquer les versions et bloquer xmlrpc.php

Dans le bloc http {} de /etc/nginx/nginx.conf, ajoutez la directive qui supprime la version Nginx des en-têtes et des pages d'erreur :

server_tokens off;

Dans le bloc server {}, masquez la version PHP renvoyée dans X-Powered-By :

fastcgi_hide_header X-Powered-By;

Pour bloquer xmlrpc.php, vecteur principal des attaques par force brute et amplification DDoS :

location = /xmlrpc.php {
    deny all;
    access_log off;
    log_not_found off;
}

En-têtes HTTP de sécurité

Ces quatre en-têtes couvrent le clickjacking, le MIME sniffing, la fuite de référent et les API navigateur inutiles :

add_header X-Frame-Options SAMEORIGIN always;
add_header X-Content-Type-Options nosniff always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

Le mot-clé always force Nginx à envoyer ces en-têtes sur toutes les réponses, y compris les 4xx et les 5xx. Sans lui, une page d'erreur personnalisée ne les transmet pas.

Rate limiting sur wp-login.php

Définissez la zone de limitation dans le contexte http {}, puis appliquez-la sur la page de connexion :

limit_req_zone $binary_remote_addr zone=wp_login:10m rate=5r/m;

location = /wp-login.php {
    limit_req zone=wp_login burst=3 nodelay;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

Cette règle autorise 5 tentatives de connexion par minute par adresse IP, avec une tolérance de 3 requêtes en rafale. Adaptez le chemin du socket PHP à votre version installée (php8.2-fpm.sock, php8.3-fpm.sock).

Apache et .htaccess

Si votre hébergement impose Apache, les règles .htaccess couvrent le même périmètre. Placez-les en dehors des balises # BEGIN WordPress et # END WordPress pour qu'une mise à jour du core ne les écrase pas.

Protection de wp-config.php et des fichiers sensibles :

<Files "wp-config.php">
    Require all denied
</Files>

<FilesMatch "(readme\.html|license\.txt|debug\.log|.*\.sql|.*\.bak)$">
    Require all denied
</FilesMatch>

Désactivation du directory listing et blocage de l'exécution PHP dans le dossier uploads :

Options -Indexes

<Directory "/var/www/html/wp-content/uploads">
    <Files "*.php">
        Require all denied
    </Files>
</Directory>

Pour masquer la bannière de version Apache, éditez /etc/apache2/conf-available/security.conf :

ServerTokens Prod
ServerSignature Off

Rechargez la configuration avec systemctl reload apache2.

Durcir wp-config.php

Ces quatre constantes s'ajoutent avant la ligne /* That's all, stop editing! */. Leur rôle est détaillé dans la référence officielle WordPress sur le durcissement.

// Désactiver l'éditeur de fichiers dans l'admin (thèmes, plugins)
define('DISALLOW_FILE_EDIT', true);

// Bloquer aussi l'installation et la mise à jour via l'interface
define('DISALLOW_FILE_MODS', true);

// Forcer HTTPS pour toutes les pages wp-admin
define('FORCE_SSL_ADMIN', true);

// Désactiver le mode debug en production
define('WP_DEBUG', false);

DISALLOW_FILE_EDIT supprime les capacités edit_themes, edit_plugins et edit_files pour tous les comptes, y compris les administrateurs. DISALLOW_FILE_MODS va plus loin : un compte compromis ne peut plus installer de plugin malveillant via le tableau de bord.

Pour les clés de sécurité (salts), régénérez-les en cas de suspicion de compromission de sessions : chaque nouvelle valeur invalide immédiatement toutes les sessions actives sur le site.

Template Nginx complet — 15 directives commentées

Ce bloc se place dans le server {} de votre virtual host WordPress. Testez avec nginx -t avant de recharger le service avec systemctl reload nginx.

# 1. Masquer la version Nginx dans les en-têtes et pages d'erreur
server_tokens off;

# 2. Masquer la version PHP dans X-Powered-By
fastcgi_hide_header X-Powered-By;

# 3. Bloquer le clickjacking — iframes uniquement depuis le même domaine
add_header X-Frame-Options SAMEORIGIN always;

# 4. Bloquer le MIME sniffing — le navigateur respecte le Content-Type déclaré
add_header X-Content-Type-Options nosniff always;

# 5. Contrôler la transmission du référent vers les domaines tiers
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

# 6. Désactiver les API navigateur non utilisées (caméra, micro, géoloc)
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

# 7. Forcer HTTPS pendant 2 ans, sous-domaines inclus
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

# 8. Désactiver le directory listing sur tous les répertoires
autoindex off;

# 9. Bloquer xmlrpc.php — vecteur de brute-force et amplification DDoS
location = /xmlrpc.php {
    deny all;
    access_log off;
}

# 10. Bloquer l'accès aux fichiers de configuration et documentation
location ~ /(wp-config\.php|readme\.html|license\.txt) {
    deny all;
}

# 11. Bloquer les fichiers cachés (.htaccess, .env, .git/config)
location ~ /\. {
    deny all;
    access_log off;
}

# 12. Bloquer l'exécution PHP dans le dossier uploads
location ~* /wp-content/uploads/.*\.php$ {
    deny all;
}

# 13. Bloquer l'accès direct aux scripts internes de wp-includes
location ~* /wp-includes/.*\.php$ {
    deny all;
}

# 14. Bloquer les fichiers de sauvegarde et journaux exposés
location ~* \.(sql|bak|log|ini|sh|cfg|env)$ {
    deny all;
    access_log off;
}

# 15. Rate limiting sur wp-login.php : 5 tentatives/minute par IP
limit_req_zone $binary_remote_addr zone=wp_login:10m rate=5r/m;
location = /wp-login.php {
    limit_req zone=wp_login burst=3 nodelay;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

Vérification post-durcissement

Trois contrôles permettent de confirmer que les directives sont actives :

  1. securityheaders.com — analyse les en-têtes HTTP renvoyés. Une note A ou A+ confirme que X-Frame-Options, X-Content-Type-Options et Referrer-Policy sont présents et correctement formulés.
  2. Mozilla Observatory — analyse plus large : en-têtes, cookies, HTTPS et Content Security Policy. Disponible sur observatory.mozilla.org.
  3. Sentinelo Scan — vérifie les 19 contrôles WordPress, dont l'exposition de xmlrpc.php, la présence de readme.html et les versions serveur exposées. Gratuit, sans inscription, avec un quota horaire.

Pour confirmer la protection de wp-config.php depuis un terminal :

curl -I https://votre-site.fr/wp-config.php

La réponse attendue est un 403. Un 200 signifie que la protection n'est pas active.

Si l'analyse remonte des vulnérabilités dans les plugins installés, le guide détecter et corriger les plugins WordPress vulnérables couvre l'étape suivante du durcissement.

La configuration serveur traite la surface d'attaque externe. Pour la surveillance continue — mises à jour automatiques, scans hebdomadaires et alertes sur les nouvelles CVE WordPress — la plateforme de sécurité Sentinelo automatise les 19 contrôles sur l'ensemble de votre parc.

Questions fréquentes

Faut-il désactiver xmlrpc.php sur tous les sites WordPress ?

Si votre site n'utilise pas les pingbacks, l'application mobile WordPress ou un service tiers qui s'appuie sur l'API XML-RPC, bloquez xmlrpc.php au niveau serveur. La règle Nginx coûte moins d'une milliseconde et supprime un vecteur d'attaque actif. En revanche, WooCommerce et Jetpack l'utilisent dans certaines configurations : vérifiez vos dépendances avant de bloquer.

Quelle est la différence entre DISALLOW_FILE_EDIT et DISALLOW_FILE_MODS ?

DISALLOW_FILE_EDIT empêche l'édition des fichiers PHP depuis l'admin (thèmes, plugins). DISALLOW_FILE_MODS va plus loin : il bloque aussi l'installation et la mise à jour de plugins et thèmes via l'interface. Si vos mises à jour passent par un pipeline CI, activez les deux. Si vous mettez à jour manuellement depuis l'admin, activez seulement DISALLOW_FILE_EDIT.

Comment vérifier que les en-têtes HTTP de sécurité sont bien envoyés ?

Utilisez securityheaders.com ou la commande curl -I https://votre-site.fr pour consulter les en-têtes renvoyés. Vérifiez la présence de X-Frame-Options, X-Content-Type-Options et Referrer-Policy. Une note A ou A+ sur securityheaders.com confirme que les en-têtes essentiels sont corrects et bien formulés.

Peut-on recharger la configuration Nginx sans interrompre le service ?

Oui. Après modification, testez la configuration avec nginx -t. Si la commande retourne 'syntax is ok', rechargez avec systemctl reload nginx. Le rechargement est gracieux : les connexions actives ne sont pas interrompues, et la nouvelle configuration prend effet immédiatement pour les nouvelles requêtes.

Le durcissement serveur remplace-t-il un plugin de sécurité WordPress ?

Non, les deux couches sont complémentaires. Le durcissement serveur bloque les requêtes avant que PHP ne démarre : xmlrpc.php, fichiers de sauvegarde exposés, PHP dans les uploads. Un plugin de sécurité intervient au sein de WordPress pour détecter les modifications de fichiers core et surveiller les connexions. Combinez les deux pour couvrir l'ensemble de la surface d'attaque.

Un parc à superviser ? Voyez ce que Sentinelo détecte chez vous.

Scanner mon site