SecurityGuide

Audit de sécurité WordPress en 10 étapes (2026)

Sur cette page
  1. Pourquoi cet audit, en 90 minutes, maintenant
  2. 1. Vérifier les versions de PHP et WordPress
  3. 2. Auditer les plugins installés et leurs CVE
  4. 3. Tester les headers de sécurité HTTP
  5. 4. Durcir le .htaccess ou la config nginx
  6. 5. Valider la configuration SSL/TLS
  7. 6. Protéger /wp-login.php et activer la 2FA
  8. 7. Décider quoi faire de xmlrpc.php
  9. 8. Bloquer l'énumération des utilisateurs
  10. 9. Mettre en place logs et alertes
  11. 10. Sauvegardes hors site et plan de reprise
  12. Cadence d'audit recommandée
  13. Sources et pour aller plus loin

Cet audit de sécurité WordPress, c'est exactement la passe que je fais tourner sur un site, du début à la fin en environ 90 minutes. Dix étapes, chacune sous forme de fiche checklist. Chaque étape te donne la commande que je colle vraiment, plus l'outil gratuit qui fait le même contrôle sans SSH, plus un aperçu de ce à quoi ressemble un résultat sain. Fais les dix et tu fermes la porte à quelque chose comme 90 pour cent des cochonneries automatisées qui martèlent WordPress en 2026. Pas parfait, non. Mais franchement, ça n'a plus rien à voir avec l'état dans lequel la plupart des sites se trouvent aujourd'hui. Versions PHP et WordPress, CVE de plugins, headers, TLS, 2FA du login, xmlrpc, énumération des utilisateurs, logs, et des sauvegardes que tu as réellement ramenées à la vie.

The short answer

Fais tourner dix contrôles dans l'ordre sur un site WordPress en prod : versions PHP et WordPress, CVE de plugins, les six headers de sécurité HTTP, durcissement .htaccess/nginx, la configuration TLS, rate limiting du login plus 2FA, xmlrpc.php, énumération des utilisateurs, logs avec alertes, et sauvegardes hors site que tu as réellement restaurées. Chaque étape te donne la commande à coller, un outil gratuit qui fait le même contrôle sans SSH, et ce à quoi ressemble un résultat sain.

10 étapeschacune une fiche checklist
90 mindu début à la fin
~90%d'attaques automatisées bloquées
Fiche réponse : un audit WordPress en 10 étapes couvrant versions PHP et WP, CVE de plugins, headers HTTP, htaccess, TLS, 2FA du login, xmlrpc, énumération des utilisateurs, logs et sauvegardes hors site.
Les dix portes que cet audit referme. Descends la liste et un bot doit toutes les battre. PNG

Des années à nettoyer des sites WordPress piratés. Un zero-day exotique ? Presque jamais. Ils se font avoir par les trucs basiques, les réglages oubliés qui traînent partout sur le web depuis dix ans. Un vieux build PHP. Un plugin que personne n'a mis à jour. Un header manquant. Un xmlrpc.php grand ouvert, un login admin sans 2FA. Alors j'ai écrit noir sur blanc l'audit exact que je fais tourner, et ça me prend environ 90 minutes du début à la fin. Dix étapes, chacune sous forme de fiche checklist. Chaque étape te donne la commande que je colle vraiment, plus l'outil gratuit qui fait le même contrôle sans SSH, plus un aperçu de ce à quoi ressemble un résultat sain. Fais les dix et tu fermes la porte à quelque chose comme 90 pour cent des cochonneries automatisées qui martèlent WordPress en 2026. Pas parfait, non. Mais franchement, ça n'a plus rien à voir avec l'état dans lequel la plupart des sites se trouvent aujourd'hui.

Pourquoi cet audit, en 90 minutes, maintenant

Environ 43 pour cent du web ouvert tourne sous WordPress en 2026. On dirait un motif de fierté, jusqu'à ce qu'on se rappelle que les bots vont là où il y a du volume, et qu'une part de marché pareille te peint une cible dans le dos. Le calcul de l'attaquant ressemble à ça. Un seul scraper qui tape sur /xmlrpc.php et /wp-login.php peut balayer une énorme tranche de l'espace IPv4 en une semaine environ, pour quasiment rien. Ton calcul est tout aussi simple, et c'est la partie que les gens ratent. Bouche les dix trous bien connus ci-dessous et tu sors l'attaquant du chemin pas cher, complètement. Maintenant il lui faut un exploit de plugin authentifié ou un coup d'ingénierie sociale, et c'est quelque chose comme cent fois plus cher par machine. J'ai observé les deux côtés de ce marché. Les 90 minutes que tu passes cet après-midi ? C'est la semaine de nettoyage que tu ne passes pas plus tard.

1. Vérifier les versions de PHP et WordPress

Première chose que les bots fingerprintent, donc c'est la première chose que je vérifie. Un vieux build PHP ou WordPress, c'est une victoire gratuite pour eux. Mon plancher pour 2026, c'est WordPress 6.x et PHP 8.2+, et je ne descends pas en dessous. PHP 7.x est en fin de vie. Plus de correctifs de sécurité, ce qui veut dire que tu expédies des trous connus à la terre entière. Toujours dessus ? Alors c'est ça ton urgence, pas le reste de ce que tu as pu trouver aujourd'hui.

# SSH into the server
php -v
wp core version --allow-root
wp plugin list --update=available --allow-root
php 8.3.6 (cli) (built: Apr 11 2026)
WordPress 6.7.1
+----------+--------+--------+---------+
| name | status | update | version |
+----------+--------+--------+---------+
| akismet | active | none | 5.3.5 |
+----------+--------+--------+---------+

Outil gratuit : pas de SSH ? SecuChecker lit la version directement sur ton site public. Le meta generator, les balises d'assets, les noms de bundles JS. C'est exactement ce que voit l'attaquant, et c'est un peu tout l'intérêt.

Action si c'est obsolète : wp core update, puis wp plugin update --all. Un avertissement, et je l'ai appris à mes dépens. Si tu fais tourner des plugins payants, pousse d'abord sur du staging. Les mises à jour du core sont en général sans douleur. Ce sont les add-ons premium qui pètent sur un bump mineur.

2. Auditer les plugins installés et leurs CVE

Chaque plugin, c'est une porte de plus que quelqu'un doit garder. Et d'expérience, c'est de là que viennent les vraies intrusions. Pas le core. Les plugins. Alors j'applique une règle dure ici : rien de dormant, rien qui traîne une CVE critique non patchée. Un plugin qui s'est tu depuis 18 mois compte comme dormant à mes yeux. La règle est enfreinte ? Ça dégage, ou ça se corrige aujourd'hui. Pas de troisième option.

wp plugin list --field=name | xargs -I {} \
  curl -s "https://patchstack.com/api/v1/vulnerabilities?slug={}" | jq '.[] | select(.cvss_score > 7)'
contact-form-7 : OK (last update 2026-04-12)
yoast-seo : OK (last update 2026-05-02)
broken-plugin : ! CVE-2025-3421 CVSS 8.4 - NOT PATCHED

Outils gratuits : je recoupe avec trois sources, parce qu'aucun flux unique n'a tout : Patchstack Database, WPScan et Wordfence Threat Intelligence.

Action : chaque plugin « j'en aurai peut-être besoin un jour » que tu n'utilises pas activement ? Supprime-le. Désactivé, ce n'est pas sûr. Le code reste sur le disque et se fait toujours compromettre. Et quand un plugin critique n'a pas de correctif, ne reste pas dessus. Trouve un remplaçant maintenu, ou ouvre un ticket urgent chez le dev. Attendre en espérant, c'est comme ça que les brèches arrivent.

3. Tester les headers de sécurité HTTP

Dix minutes devant toi et rien d'autre ? Passe-les ici. Les headers, c'est la victoire la moins chère de toute la liste. Rien sur ton site ne casse, et tous les navigateurs modernes appliquent les règles gratuitement à la seconde où tu les poses. Ma base de référence 2026, c'est six d'entre eux. Le one-liner curl ci-dessous te dit d'un coup lesquels manquent.

curl -sI https://your-site.com/ | grep -iE \
  "(strict-transport-security|content-security-policy|x-frame-options|x-content-type-options|referrer-policy|permissions-policy)"
strict-transport-security: max-age=31536000; includeSubDomains
content-security-policy: default-src 'self'; ...
x-content-type-options: nosniff
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
x-frame-options: SAMEORIGIN

Outils gratuits : tu préfères une UI ? HTTP Headers Checker par PeopleAreGeek, plus securityheaders.com et Mozilla Observatory pour un deuxième avis noté.

Action : ajoute ceux qui manquent au niveau du serveur, nginx add_header ou Apache Header always set. Cinq des six, c'est à poser et oublier. CSP, c'est celui qui mord. Expédie-le trop serré et tu vas éteindre tes propres polices, images et analytics en un seul déploiement. Alors fais-le tourner en Content-Security-Policy-Report-Only pendant deux semaines d'abord, regarde les rapports de violation arriver, et seulement après applique-le. Je n'ai jamais regretté l'attente une seule fois. J'ai par contre carrément regretté de l'avoir sautée.

Checklist des six headers de sécurité HTTP à poser en 2026 : HSTS, Content-Security-Policy, X-Frame-Options, X-Content-Type-Options, Referrer-Policy et Permissions-Policy, avec le CSP signalé comme celui à déployer d'abord en mode report-only.
La base de six headers. Cinq sont à poser et oublier. CSP, c'est celui que tu expédies d'abord en report-only. PNG

4. Durcir le .htaccess ou la config nginx

Quelques trous que WordPress ne peut tout simplement pas boucher tout seul. Ils vivent sous l'appli, en bas, au niveau du serveur web. Une poignée de règles dans le .htaccess (Apache) ou ta config nginx s'en occupe. Le gros morceau, c'est de tuer l'exécution PHP à l'intérieur de /uploads/. Ce seul bloc stoppe le classique « tu uploades une image, t'obtiens un shell » que je vois encore en nettoyage après nettoyage après nettoyage.

# Apache (.htaccess)
# Block direct access to sensitive files
<FilesMatch "^(wp-config\.php|readme\.html|license\.txt|\.htaccess)$">
  Require all denied
</FilesMatch>

# Disable directory listing
Options -Indexes

# Block PHP execution in /uploads/
<Directory "wp-content/uploads">
  <FilesMatch "\.php$">
    Require all denied
  </FilesMatch>
</Directory>
# nginx equivalent
location ~* /(wp-config\.php|readme\.html|license\.txt) {
    deny all;
}
location /wp-content/uploads/ {
    location ~ \.php$ { deny all; }
}
autoindex off;

Outil gratuit : une seule directive tapée de travers peut mettre tout ton site en 500. Alors si les regex te rendent nerveux, laisse notre .htaccess Generator par PeopleAreGeek construire les règles proprement.

Vérification : ne lui fais pas confiance tant que tu ne l'as pas titillé. curl https://your-site.com/readme.html doit revenir en 403, et curl https://your-site.com/wp-content/uploads/test.php aussi. Si l'un des deux sert encore quelque chose, la règle ne s'est pas chargée. Vérifie que tu as bien édité le fichier que le serveur lit, et pas une copie voisine.

5. Valider la configuration SSL/TLS

N'importe qui peut obtenir un certificat valide de nos jours. Cette partie-là est gratuite et facile. Le vrai indicateur, c'est de savoir s'il continue à se renouveler sans toi, parce que la panne que j'ai vue le plus souvent n'est pas un piratage du tout. C'est un cron job qui s'est arrêté en silence et un certificat qui a expiré à minuit un samedi. En 2026, je veux TLS 1.3 activé. 1.0 et 1.1 coupés pour de bon. Plus de 30 jours au compteur, et OCSP stapling activé.

# Quick command-line check
echo | openssl s_client -servername your-site.com -connect your-site.com:443 2>/dev/null \
  | openssl x509 -noout -dates -issuer
nmap --script ssl-enum-ciphers -p 443 your-site.com
notBefore=Apr 2 00:00:00 2026 GMT
notAfter=Jul 1 23:59:59 2026 GMT
issuer=C = US, O = Let's Encrypt, CN = R3
TLSv1.3:
 ciphers:
 TLS_AES_256_GCM_SHA384 (ecdh_x25519) - A
 TLS_CHACHA20_POLY1305_SHA256 (ecdh_x25519) - A

Outils gratuits : SSL Certificate Checker et TLS Version Selector par PeopleAreGeek, puis fais tourner Qualys SSL Labs pour le rapport noté complet. Ne te contente pas de moins qu'un A. Un A+ si tu peux l'avoir, et en général tu peux.

Action : noté en dessous de A ? Récupère le snippet nginx ou Apache que crache notre TLS Version Selector et colle-le. Ça referme l'essentiel de l'écart, là, tout de suite. Ensuite, et je ne peux pas le dire assez fort, mets un monitor qui te ping 30 jours avant l'expiration du certificat. Le renouvellement que tu supposes automatique est celui qui échoue en silence.

6. Protéger /wp-login.php et activer la 2FA

Ouvre n'importe quel access log sur un site WordPress en prod et tu vas le voir. Un filet continu de bots qui balancent des mots de passe sur la page de login, toute la journée, tous les jours. Cible numéro un, point final. Je m'appuie sur quelques trucs pour faire retomber la pression. Le rate limiting au niveau du serveur fait l'essentiel du vrai boulot. Ensuite une 2FA à laquelle les admins ne peuvent pas se soustraire. Et si tu veux, déplacer l'URL de login hors du chemin par défaut, même si ce dernier point est optionnel et je signale un piège dessus plus bas.

# nginx rate limit on /wp-login.php
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;

location = /wp-login.php {
    limit_req zone=login burst=2 nodelay;
    fastcgi_pass php-fpm;
    # ...
}

Plugins 2FA recommandés : je dégaine Two Factor en premier. C'est l'officiel et c'est gratuit. WP 2FA est un bon choix aussi, ou la 2FA intégrée à Wordfence Premium si tu le fais déjà tourner.

Vérification : teste-le pour de vrai. Ne suppose pas. Balance dix logins ratés depuis une IP jetable en moins d'une minute et assure-toi que le onzième se fait throttler. Ensuite sors wp user list --role=administrator et parcours la liste. Chaque admin a besoin de la 2FA, y compris le vieux compte « temp » de 2023 que tout le monde a oublié. Celui-là, c'est toujours la porte d'entrée.

Avertissement : saute le renommage de l'URL de login si tu utilises l'appli mobile WordPress ou Jetpack. Les deux s'attendent au chemin par défaut, et tu vas les casser sans le moindre message d'erreur pour te prévenir. J'ai déjà vu quelqu'un cramer tout un après-midi sur une appli « cassée » qui n'était qu'un login renommé.

7. Décider quoi faire de xmlrpc.php

Voici la version honnête. xmlrpc.php existe pour l'appli mobile WordPress, quelques fonctions de Jetpack, et des pingbacks dont personne ne s'est plaint de l'absence depuis des années. Pour la plupart des sites, c'est de la surface d'attaque pure. C'est le fichier qui permet à un bot de tester des centaines de mots de passe en une seule requête via system.multicall. Alors par défaut, je le mure complètement. Si tu en as vraiment besoin, ne l'ouvre pas au monde entier. Épingle-le à la poignée d'IP qui l'appellent réellement.

# nginx: block entirely
location = /xmlrpc.php {
    deny all;
}

# Variant: allow Jetpack only
location = /xmlrpc.php {
    allow 192.0.64.0/18;  # Jetpack
    deny all;
}
# Apache (.htaccess)
<Files xmlrpc.php>
  Require all denied
</Files>

Vérification : tape dessus avec curl -X POST https://your-site.com/xmlrpc.php et tu veux un 403 en retour. Tu as pris la voie de l'allow-list ? Les plages d'IP Jetpack actuelles vivent dans leur doc officielle, et oui, elles changent. Alors ne les code pas en dur pour ensuite t'en aller.

8. Bloquer l'énumération des utilisateurs

Par défaut, WordPress va joyeusement filer tes noms d'utilisateur via deux canaux. ?author=N renvoie vers le slug de login de l'utilisateur N. Et /wp-json/wp/v2/users balance tout simplement le lot en JSON. Les gens sous-estiment ça parce que c'est « juste » une fuite de nom d'utilisateur. Mais c'est tout l'enjeu. Une fois qu'un bot connaît ton vrai pseudo admin, il arrête de deviner « admin » et pointe chaque tentative de mot de passe sur le compte qui existe réellement. Tu lui as donné la moitié du login.

# Block ?author=N (Apache .htaccess)
RewriteEngine On
RewriteCond %{QUERY_STRING} ^author=\d+ [NC]
RewriteRule ^ /? [L,R=301]

# Block /wp-json/wp/v2/users (child theme functions.php)
add_filter('rest_endpoints', function($endpoints) {
    if (isset($endpoints['/wp/v2/users'])) {
        unset($endpoints['/wp/v2/users']);
    }
    if (isset($endpoints['/wp/v2/users/(?P<id>[\d]+)'])) {
        unset($endpoints['/wp/v2/users/(?P<id>[\d]+)']);
    }
    return $endpoints;
});
curl https://your-site.com/?author=1 -> 301 to /
curl https://your-site.com/wp-json/wp/v2/users -> 401 or 404

Outil gratuit : pas envie de tester les deux endpoints à la main ? SecuChecker les frappe pour toi et te dit franchement si ton nom d'utilisateur admin fuit.

9. Mettre en place logs et alertes

Pas de logs ? Alors tu découvres que tu t'es fait pirater le jour où ta page d'accueil se fait remplacer par le drapeau de quelqu'un. Des logs plus une alerte qui marche, et tu le découvres dans l'heure, tant que c'est encore une tentative de login bizarre et pas une prise de contrôle complète. Cet écart, c'est tout le boulot. C'est aussi la partie que tout le monde saute parce que rien ne brûle encore. Jusqu'au moment où si.

  • Logs serveur : garde access.log et error.log nginx/Apache en rotation, et conserve au moins 90 jours. Le jour où tu en as besoin, c'est le jour où tu regretteras d'avoir raccourci à sept.
  • Logs applicatifs : fais tourner WP Activity Log ou Wordfence pour pouvoir voir qui a touché à quoi. Un plugin remplacé, ou un compte apparu du jour au lendemain. Cette trace, c'est de l'or pendant un incident.
  • Monitoring d'uptime et d'intégrité : SecurityWatch par PeopleAreGeek surveille cinq choses à la fois (uptime, un hash de défaçage, l'expiration TLS, des headers qui reviennent en arrière en silence, ta version WP qui dérive) et ping un webhook Slack ou Discord à l'instant où l'un d'eux glisse.
# Logrotate nginx (Debian/Ubuntu)
sudo nano /etc/logrotate.d/nginx
# Check "rotate 90" and "compress"
sudo logrotate -d /etc/logrotate.d/nginx

Outil gratuit : SecurityWatch monte la garde sans backend à materner. Il tourne dans le navigateur à partir d'une watchlist en localStorage, ce qui est exactement ce qu'il faut si tu jongles avec 1 à 10 sites WP et que tu ne veux pas un serveur de plus à entretenir.

10. Sauvegardes hors site et plan de reprise

Une sauvegarde que tu n'as jamais restaurée, ce n'est pas une sauvegarde. C'est une impression. J'ai vu des gens ouvrir leur « sauvegarde nocturne » en plein incident et trouver un dossier de fichiers de zéro octet remontant à des mois. Le bon vieux 3-2-1 tient toujours en 2026 : trois copies, deux supports différents, l'un d'eux hors site et hors de la zone d'impact. Et restaure-la chaque trimestre, pour de vrai, parce que la seule sauvegarde qui compte est celle que tu as réellement ramenée à la vie.

# Manual backup via WP-CLI
wp db export ~/backup-$(date +%F).sql
tar -czf ~/wp-content-$(date +%F).tar.gz wp-content/

# Upload to S3 / Backblaze / OVH Object Storage
rclone copy ~/backup-$(date +%F).sql remote:wp-backups/
rclone copy ~/wp-content-$(date +%F).tar.gz remote:wp-backups/

Plugins recommandés : UpdraftPlus (gratuit, S3/Google Drive/Dropbox), Snapshot Pro ou BlogVault pour les sites e-commerce.

Test trimestriel : dépose la sauvegarde la plus récente sur une machine de staging, clique partout jusqu'à être sûr qu'elle marche vraiment, et chronomètre combien de temps tout ça a pris. Ce chiffre, c'est ton RTO, et tu veux le connaître avant d'être sous pression, pas pendant. Chaque heure qu'elle met à revenir est une heure d'indisponibilité, de ventes perdues, d'un client qui demande pourquoi le site est encore down.

Cadence d'audit recommandée

Fais ça une fois et passe ton chemin, et tu t'es acheté peut-être un trimestre de sécurité avant que la dérive ne se réinstalle. La cadence que je tiens ressemble à ça. Les dix étapes complètes au lancement et une fois par trimestre. Une petite passe mini sur les étapes 1, 2, 3 et 5 après chaque mise à jour majeure de WordPress, parce que ce sont celles que les mises à jour ont tendance à défaire dans ton dos. Et SecurityWatch ou quelque chose du genre qui tourne en arrière-plan le reste du temps. Un site surveillé en continu se fait toucher à peu près dix fois moins souvent qu'un site qu'on regarde une fois par an, et franchement peu importe la stack qu'il y a dessous. Quelqu'un qui fait attention, c'est l'essentiel de la différence.

Sources et pour aller plus loin

Questions fréquentes

Combien de temps prend un audit complet pour un débutant ?

Prévois 3 à 4 heures la première fois, honnêtement. L'essentiel, c'est juste de comprendre où tout se trouve sur le serveur. Une fois que tu connais les chemins, ça tombe à 60 à 90 minutes. Les deux qui bouffent toujours le chrono ? L'étape 3, où le CSP peut avaler 30 minutes de réglage, et l'étape 10, où un vrai test de restauration tourne autour d'une heure. Ne bâcle pas ces deux-là. Ce sont celles qui comptent le plus quand les choses partent en vrille.

Devrais-je vraiment désactiver xmlrpc.php ?

Pour la plupart des gens, oui. Bloque-le sans hésiter. Le piège, c'est juste de vérifier que tu ne t'en sers pas vraiment. L'appli mobile WordPress, Jetpack en mode connecté, les outils de publication externes comme Microsoft Word ou MarsEdit, ils s'appuient tous dessus. Si rien de tout ça n'est dans ta vie, le bénéfice est réel (ça tue l'astuce system.multicall qui permet à une requête de tester une pile de mots de passe) et tu ne perds absolument rien.

Quels plugins de sécurité utiliser en 2026 ?

Pour un site normal, le Wordfence gratuit te mène à peu près à 80 pour cent du chemin à lui seul. Firewall applicatif, scan de fichiers, 2FA, terminé. Si tu fais tourner une boutique ou un truc qui ne peut vraiment pas tomber, j'ajouterais Patchstack ou WPScan Premium par-dessus pour être au courant des CVE de plugins le jour où elles sortent, et je mettrais un WAF en frontal comme Cloudflare Pro ou Sucuri. Un truc que je dirai quand même. N'empile pas cinq plugins de sécurité. Ils commencent à se battre entre eux et tu finis plus mal loti qu'avec un seul auquel tu fais réellement confiance.

Mon hébergeur dit qu'il gère la sécurité, est-ce suffisant ?

À moitié vrai, et la moitié qu'ils passent sous silence est celle qui te fait pirater. Un hébergeur managé comme Kinsta, WP Engine ou OVH WordPress s'occupe du socle serveur (mises à jour PHP, le firewall réseau, les sauvegardes) et ils sont vraiment bons là-dessus. Ce à quoi ils ne peuvent pas toucher, c'est tout ce qui est à l'intérieur de ton install. Un plugin vulnérable que tu as choisi. Un admin sans 2FA. ?author=N qui file ton nom d'utilisateur à qui le demande. Les étapes 2, 6, 7 et 8 ici sont à toi, peu importe qui t'héberge. On gère la sécurité veut presque toujours dire on gère notre couche.

Comment savoir si mon site est déjà compromis ?

Quelques signes que j'ai appris à croire. Du trafic sortant vers des IP que tu ne reconnais pas. Des comptes admin que tu jures n'avoir jamais créés. Des pages pharma ou casino planquées dans /wp-content/uploads/, et des fichiers PHP aux noms de charabia aléatoire posés à la racine de ton site. N'importe lequel de ces signes, traite-le comme un oui jusqu'à preuve du contraire. Pour confirmer vite et gratis, balance l'URL dans VirusTotal, fais-la passer par Sucuri SiteCheck, puis lâche le scan de fichiers Wordfence sur l'install. Clean ? Super, tu as perdu dix minutes. Si ça ne l'est pas, eh bien, tu viens de l'attraper tôt.