Bloquer complètement l’énumération des utilisateurs WordPress avec Nginx ou Apache

Lorsque vous gérez un site WordPress, la sécurité doit être une priorité. Parmi les failles les plus méconnues, il existe ce qu’on appelle l’énumération des utilisateurs.

Mais qu’est-ce que c’est exactement ?

En résumé, l’énumération des utilisateurs consiste à découvrir publiquement les noms d’utilisateurs de votre site WordPress. Pourquoi est-ce un problème ? Parce que si un pirate connaît déjà votre nom d’utilisateur, il ne lui reste plus qu’à deviner ou voler le mot de passe. En d’autres termes, c’est comme si vous laissiez la moitié de la clé de votre maison sur la porte.

Comment fonctionne l’énumération des utilisateurs ?

Par défaut, WordPress crée certaines adresses (URLs) qui permettent de révéler les noms d’utilisateurs. Ces URLs sont accessibles à tout le monde sur Internet, même sans être connecté à votre site.

Voici quelques exemples (remplacés par example.com) :

  • https://example.com/?author=1
  • https://example.com/author/admin
  • https://example.com/?author={num:1}
  • https://example.com/wp-sitemap-users-1.xml
  • https://example.com/wp-json/wp/v2/users

Si vous ouvrez ces adresses dans votre navigateur, vous risquez de voir apparaître des informations sur vos utilisateurs WordPress (souvent l’administrateur du site). Et vous pouvez imaginer ce que cela représente pour un pirate : un point d’entrée facile.

Pourquoi est-ce dangereux ?

  • Les pirates peuvent scanner automatiquement ces URLs avec des outils comme WPScan.
  • Une fois le nom d’utilisateur trouvé, ils peuvent lancer des attaques par force brute pour tester des milliers de mots de passe.
  • Cela augmente fortement les risques de piratage de votre site.

En clair : si vous laissez l’énumération des utilisateurs active, vous donnez gratuitement une information critique aux attaquants.

Comment bloquer l’énumération des utilisateurs avec Nginx

Heureusement, si votre site est hébergé sur un serveur utilisant Nginx, vous pouvez bloquer totalement ces tentatives. Il suffit d’ajouter quelques règles simples dans votre configuration.

Voici un extrait que vous pouvez intégrer dans votre fichier de configuration Nginx (par exemple dans le server {}) :

# Bloquer l’énumération via le paramètre ?author=
if ($args ~* "^author=([0-9]+|{num:[0-9]+)") {
    return 444;
}

# Bloquer l’énumération via /author/nomutilisateur
if ($request_uri ~ "/author/") {
    return 444;
}

# Bloquer les sitemaps qui révèlent les utilisateurs
# y compris la version encodée avec %2d
if ($request_uri ~ "wp-sitemap-users-[0-9]+.xml") {
    return 444;
}

# Bloquer l’API REST WordPress qui expose les utilisateurs
if ($request_uri ~ "/wp-json/wp/v2/users") {
    return 444;
}
Code language: PHP (php)

Explication simple :

  • return 444; : c’est une réponse spéciale de Nginx qui interrompt la connexion sans explication. Résultat : l’attaquant reçoit une erreur et ne peut pas continuer.
  • Les règles ci-dessus couvrent tous les principaux moyens connus d’énumération des utilisateurs WordPress.

Comment bloquer l’énumération des utilisateurs avec Apache

Si vous utilisez Apache, il suffit d’ajouter l’équivalent de nos règles dans le fichier .htaccess :

# ----------------------------------------------------------------------
# Bloquer l’énumération des utilisateurs WordPress (.htaccess)
# ----------------------------------------------------------------------
# Placez ce bloc AU-DESSUS du bloc # BEGIN WordPress par défaut
# Testé pour Apache 2.4+. Retourne 403 (Forbidden) pour les tentatives
# d'énumération côté public. Laisse passer les utilisateurs connectés.
# ----------------------------------------------------------------------

<IfModule mod_rewrite.c>
    RewriteEngine On

    # --------------------------------------------------------------
    # Détection "utilisateur connecté" (cookie WordPress)
    # Si le cookie 'wordpress_logged_in_' est présent, on N'APPLIQUE PAS
    # les règles de blocage pour ne pas gêner l'admin et les rédacteurs.
    # --------------------------------------------------------------
    # Astuce : on ne met PAS de RewriteCond positive ici, on ajoute plutôt
    # une condition de NON-connexion devant chaque règle de blocage.
    # Pattern robuste pour éviter les faux positifs.
    # --------------------------------------------------------------

    # 1) Blocage de ?author=... (y compris variantes encodées)
    # Exemples: /?author=1  |  /?author=12  |  /?author=%7Bnum%3A1%7D
    RewriteCond %{HTTP_COOKIE} !(^|;\ *)wordpress_logged_in_ [NC]
    RewriteCond %{QUERY_STRING} (^|&)author=([0-9]+|%7Bnum%3A[0-9]+%7D) [NC]
    RewriteRule ^ - [F,L]

    # 2) Blocage des jolis permaliens /author/nom
    RewriteCond %{HTTP_COOKIE} !(^|;\ *)wordpress_logged_in_ [NC]
    RewriteRule ^author/ - [F,L,NC]

    # 3) Blocage du sitemap des utilisateurs (WordPress 5.5+)
    # Exemple: /wp-sitemap-users-1.xml
    RewriteCond %{HTTP_COOKIE} !(^|;\ *)wordpress_logged_in_ [NC]
    RewriteRule ^wp-sitemap-users-[0-9]+\.xml$ - [F,L,NC]

    # 4) Blocage de l'endpoint REST qui expose les utilisateurs:
    # /wp-json/wp/v2/users (et variante avec slash final)
    RewriteCond %{HTTP_COOKIE} !(^|;\ *)wordpress_logged_in_ [NC]
    RewriteRule ^wp-json/wp/v2/users/?$ - [F,L,NC]

</IfModule>

# Sécurité complémentaire si mod_authz_core est dispo (Apache 2.4+)
# Retourne 403 même si mod_rewrite est désactivé/absent.
<IfModule mod_authz_core.c>
    <FilesMatch "^wp-sitemap-users-[0-9]+\.xml$">
        Require all denied
    </FilesMatch>
</IfModule>Code language: PHP (php)

Pourquoi 403 (Forbidden) ?

Apache n’a pas de code « 444 » façon Nginx. Le flag [F] (Forbidden) est standard, clair et fonctionne partout. C’est rapide et ne révèle rien d’utile à l’attaquant.

Comment vérifier que cela fonctionne ?

Une fois les règles appliquées, essayez d’ouvrir les URLs listées plus haut dans votre navigateur. Vous devriez obtenir :

  • Soit une page d’erreur,
  • Soit un blocage direct (si vous êtes derrière Cloudflare par exemple).

Dans les deux cas, cela veut dire que vos utilisateurs WordPress sont protégés et invisibles aux yeux des curieux.

Bonnes pratiques complémentaires

  • Désactivez les archives auteur si vous ne les utilisez pas (côté thème/SEO) pour réduire la surface d’attaque et éviter des liens morts.
  • Comptes admin : n’utilisez jamais admin comme identifiant.
  • Mots de passe : longs, uniques et 2FA activée.
  • Limiter les tentatives : un WAF (hébergeur/CDN) ou un plugin sérieux peut bloquer la force brute au niveau applicatif.

Log des énumérations et blocage avec fail2ban

Nous allons aller plus loin : bloquer les requêtes d’énumération, c’est bien, mais bloquer les responsables au niveau du serveur avec fail2ban, ce sera encore mieux.

Nous allons donc identifier les requêtes qui tentent de lister vos comptes WordPress. Nous les enregistrons dans un fichier dédié, puis on ferme la connexion sans réponse. Cela aide à détecter et à bloquer les robots malveillants sans polluer votre log principal.

Configuration du log sous NginX

Placez le map et le log_format dans le contexte global http {}.
Placez les access_log et la règle if ($user_enum) dans le server {} correspondant.

# ---------- à mettre dans http { ... } ----------
# map : calcule la variable user_enum à 1 si matching
map $args $user_enum {
    default 0;

    # ?author=1 ou ?author=%7Bnum%3A1%7D  (encoded {num:1})
    ~*author=([0-9]+|%7Bnum%3A[0-9]+%7D) 1;
}

map $request_uri $user_enum_uri {
    default 0;

    # permalinks /author/slug
    ~*/author/ 1;

    # sitemaps users: wp-sitemap-users-1.xml (et encodée)
    ~*wp-sitemap-users-[0-9]+\.xml 1;

    # REST API users endpoint
    ~*/wp-json/wp/v2/users 1;
}

# combine maps (si l'une des conditions vaut 1 => on loggue + bloquer)
map "$user_enum:$user_enum_uri" $is_user_enum {
    default 0;
    "~*1:0" 1;
    "~*0:1" 1;
    "~*1:1" 1;
}

# format de log dédié (écrit uniquement les infos utiles)
log_format user_enum_fmt '$remote_addr - $remote_user [$time_local] '
                        '"$request" $status $body_bytes_sent '
                        '"$http_referer" "$http_user_agent" '
                        'rt=$request_time host=$host';

# ---------- à mettre dans server { ... } ----------
# Log principal comme d'habitude
access_log /var/log/nginx/access.log combined;

# Log dédié aux tentatives d'énumération (écrit seulement si $is_user_enum != 0)
access_log /var/log/nginx/user-enum.log user_enum_fmt if=$is_user_enum;

# Bloquer / couper la connexion si tentative détectée
# On renvoie 444 (close connection). 444 est non-standard Nginx.
if ($is_user_enum) {
    return 444;
}Code language: PHP (php)

Remarques techniques :

  • map ne peut être défini que dans le bloc http.
  • access_log ... if=... permet d’écrire seulement les entrées ciblées.
  • Le code 444 ferme la connexion sans réponse. C’est pratique contre les robots.

Configuration logrotate pour /var/log/nginx/user-enum.log

Dans notre configuration NginX, notre fichier de logs est /var/log/nginx/user-enum.log. Assurez-vous que l’utilisateur Nginx (www-data ou nginx) peut écrire dedans.

Nous allons configurer logrotate pour /var/log/nginx/user-enum.log afin d’éviter d’emplir le disque :

nano /etc/logrotate.d/nginx-user-enum

Et on y ajoute :

/var/log/nginx/user-enum.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    create 0640 www-data adm
    sharedscripts
    postrotate
        [ -s /run/nginx.pid ] && kill -USR1 `cat /run/nginx.pid`
    endscript
}Code language: JavaScript (javascript)

Whitelist d’admin par IP

Si vous voulez que certaines IPs (ex. votre IP d’admin) ne soient jamais bloquées, mappez-les :

# Dans http {}
geo $admin_whitelist {
    default 0;
    203.0.113.5 1;   # exemple d'IP admin
}

# Puis ajustez $is_user_enum pour prendre en compte la whitelist
map "$is_user_enum:$admin_whitelist" $is_user_enum_final {
    default $is_user_enum;
    "~*:1" 0;   # si admin_whitelist==1 => ne pas bloquer
}
# Utiliser $is_user_enum_final dans access_log if= et if ()Code language: PHP (php)

Exemple Fail2Ban pour bannir automatiquement

Vous pouvez corréler user-enum.log avec Fail2Ban et bannir les IPs qui insistent.

/etc/fail2ban/filter.d/nginx-user-enum.conf

[Definition]
# matche les lignes du user-enum.log contenant la requête bloquée
failregex = ^<HOST> - .* "(GET|POST) .*" 444
ignoreregex =
Code language: PHP (php)

/etc/fail2ban/jail.d/nginx-user-enum.local

[nginx-user-enum]
enabled = true
filter = nginx-user-enum
logpath = /var/log/nginx/user-enum.log
maxretry = 5
bantime = 86400
findtime = 3600
action = iptables-multiport[name=nginx-user-enum, port="http,https"]
Code language: JavaScript (javascript)

Cela bannit une IP après 5 tentatives dans l’heure. Adaptez maxretry et bantime.

Conclusion

L’énumération des utilisateurs est une faille trop souvent ignorée. Pourtant, elle facilite énormément le travail des pirates. En bloquant ces requêtes directement au niveau de Nginx, vous fermez une porte d’entrée critique pour protéger votre site WordPress.

Besoin d’un partenaire fiable pour votre projet WordPress/WooCommerce ? Je mets mon expertise à votre service pour des résultats concrets.

Bénéficiez d’un accompagnement personnalisé »

Matt

Développeur certifié WordPress & WooCommerce chez Codeable, administrateur système et enseignant-chercheur, je mets mon expertise au service de vos projets web.

Ma priorité : des sites performants, fiables et sécurisés, pensés pour offrir la meilleure expérience utilisateur. J’accompagne chaque client avec écoute et pédagogie, pour transformer vos idées en solutions concrètes et durables.

Profitez de solutions WordPress et WooCommerce sur-mesure, pensées pour optimiser durablement votre site.
Explorez les leviers pour booster l’impact de votre site web.

Opinions