API REST WordPress abusée : utilisateurs créés, contenu injecté
L'API REST WordPress (`/wp-json/`) n'est pas une faille par défaut. Elle le devient quand l'enregistrement est ouvert, qu'un plugin expose `users` trop largement, ou qu'une clé d'application survit au mot de passe. Voici comment le voir, fermer l'entrée, et lister ce qui a déjà été créé.
L'endpoint users peut être ouvert trop largement. Fermez l'enregistrement, listez les comptes récents, restreignez l'API après nettoyage si vous n'en avez pas besoin.
Ce que l'endpoint users raconte déjà
Sur beaucoup de sites, `https://votre-domaine.fr/wp-json/wp/v2/users` répond encore avec une liste d'identifiants, de slugs et parfois d'avatars. Ce n'est pas « le piratage » : c'est une surface. Un bot s'en sert pour tester des mots de passe, pour créer un compte si l'enregistrement est ouvert, ou pour viser un login connu. Fermer cet énumération après coup ne retire pas les comptes déjà posés.
Le signal utile n'est pas « l'API existe » — elle existe sur tout WordPress récent. C'est : des utilisateurs dont vous n'avez pas la création, des articles publiés sans passage par l'admin, des commentaires ou des médias poussés par `POST` alors que personne n'était devant l'écran. Croisez la date de ces objets avec les journaux d'accès sur `/wp-json/`.
Si l'admin est inaccessible ou lente, WP-CLI (`wp user list`, `wp post list --post_status=publish`) donne le même inventaire sans ouvrir le navigateur. Ce n'est pas un nettoyage, c'est le constat. Voir WP-CLI pour constater.
Enregistrement ouvert et rôles par défaut
Réglages → Général → « Tout le monde peut s'enregistrer » est le cas le plus bête et le plus fréquent. Le rôle par défaut « Abonné » n'est pas anodin si un plugin élève ensuite les privilèges, ou si l'API d'un constructeur permet à un abonné d'écrire. Fermez l'enregistrement sauf besoin métier réel (espace membre, WooCommerce a son propre flux).
WooCommerce crée des clients à la commande : ce n'est pas « l'enregistrement ouvert ». Distinguez les deux. Un pic de clients avec des emails jetables oriente plutôt vers un test de checkout ou un abus de formulaire ; un pic d'administrateurs ou d'éditeurs oriente vers l'API ou une faille de plugin.
Un abonné devenu administrateur est un autre dossier : faille, REST mal restreinte, ou accès base. Traitez-le comme une élévation de privilège, pas comme un simple spam de comptes.
- Enregistrement global : fermé, sauf justification écrite.
- Rôle par défaut : abonné, jamais éditeur.
- Liste des users créés sur 30 jours, triée par rôle.
Contenu injecté via l'API, pas via wp-admin
Des pages de pharmacies, des articles vides avec un lien, un média PHP déguisé : l'interface d'admin peut rester « normale » si l'attaquant n'y est jamais passé. Les révisions, l'auteur technique et les user-agents `WordPress/` ou outils d'API dans les access.log le montrent. Chercher uniquement dans Apparence → Éditeur rate ce canal.
Les plugins de constructeur (Elementor, WPBakery) et les types de contenu custom exposent leurs propres routes. Une route mal capée crée un article sans que « Articles » dans le menu ne soit le chemin d'entrée. Comparez `guid` et `post_author` des contenus récents : un auteur « admin » que vous n'avez pas utilisé à cette heure est un indice.
Si le contenu injecté est du spam SEO déjà indexé, coupez la génération puis désindexez. L'API n'est que le tuyau. Le plan de site et les permaliens font le reste. Voir le guide pages spam.
Restreindre sans casser WooCommerce ou l'app mobile
Couper `/wp-json/` au pare-feu « pour être sûr » casse le checkout WooCommerce (Store API), les blocs Gutenberg, l'app officielle, Jetpack, et une bonne partie des constructeurs. Restreignez plutôt : énumération des users, création de users pour les non authentifiés, méthodes que vous n'utilisez pas. Un WAF qui bloque `users` en GET public et laisse le Store API est plus utile qu'un couteau.
Si vous n'avez aucune app, aucun headless, aucun Zapier : vous pouvez durcir fortement. Si vous avez une PWA ou un prestataire qui pousse le catalogue, dressez la liste des routes nécessaires avant de filtrer. Casser le tunnel de paiement pour « fermer l'API » est un mauvais échange — surtout si un skimmer checkout est déjà le vrai risque.
Après restriction, testez comme un client : ajout panier, paiement, compte. Puis comme un anonyme : `users` ne doit plus lister vos logins. Les deux tests, pas un seul.
Clés REST, application passwords et webhooks
WooCommerce → Réglages → Avancé → REST API : des clés `ck_` / `cs_` avec lecture ou écriture. Une clé « Zapier 2021 » ou « old ERP » avec `write` vaut un administrateur. Révoquez ce que vous ne pouvez pas nommer, tournez le reste. Les webhooks WooCommerce (commande créée, etc.) partent vers une URL : si elle a changé, les événements vont ailleurs. Même silence que pour Stripe ou PayPal.
Les application passwords WordPress (utilisateurs → profil) survivent au changement du mot de passe principal. Listez, révoquez l'inconnu. Une app « IFTTT » de 2022 est une session permanente.
Un `.env` ou un wp-config qui contient encore l'ancienne clé, un staging qui la réutilise, un prestataire qui l'a dans un tableur : tourner dans l'admin ne suffit pas si la copie vit ailleurs. Traitez les secrets comme le SMTP — chez l'émetteur, puis dans chaque consommateur.
- Clés WooCommerce : aucune sans nom et sans besoin.
- Webhooks : URL encore la vôtre.
- Application passwords : liste vide ou nommée.
Lister les comptes et révisions récents
Triez les utilisateurs par date d'enregistrement. Tout compte admin / éditeur que personne n'endosse : exportez (id, email, rôle, date), puis retirez le rôle ou supprimez après copie. Forcez la déconnexion de tous (`wp user session destroy --all` ou régénération des clés AUTH dans `wp-config`). Inverser — changer votre mot de passe en laissant le fantôme — laisse une session ouverte.
Dans les articles et pages, filtrez par date. Une page créée à 4 h avec un contenu hors sujet n'est pas « un stagiaire ». Regardez aussi les révisions : un article légitime dont le HTML a été enrichi d'un script via l'API est plus discret qu'une page neuve.
Les médias : un `*.php.jpg` ou un fichier dans `uploads` poussé par l'API est une porte, pas un simple spam. Interdire l'exécution PHP dans uploads fait partie du refermeture, pas du diagnostic.
Ce qu'un pare-feu d'API ne remplace pas
Wordfence, un WAF Cloudflare, un plugin « disable REST » : ils réduisent la surface pour la suite. Ils ne retirent pas un utilisateur déjà admin, ni un article déjà publié, ni une clé déjà émise. Les poser pendant l'attaque ajoute des faux positifs (le checkout qui casse) et du bruit dans les logs.
La faille d'origine est souvent un plugin (JWT, membership, formulaire) qui enregistre une route trop ouverte. Désactiver l'API globale et garder le plugin vulnérable, c'est fermer la fenêtre et laisser la porte. Comparez le plugin à sa version officielle, ou retirez-le.
xmlrpc.php est un canal distinct. S'il est encore ouvert avec `system.multicall`, traitez-le à part. Ce n'est pas l'API REST, mais le résultat se ressemble : brute force, pingbacks, parfois création de contenu.
Après nettoyage : qui a encore le droit d'écrire
Dressez la liste courte : qui a besoin de `edit_posts`, qui a besoin d'une clé, quelle app appelle encore `/wp-json/`. Tout le reste : lecture seule ou rien. Relisez cette liste une semaine plus tard — les comptes « temporaires » du prestataire ont la vie dure.
Si le site est headless ou branché à une app, documentez les routes. Le prochain incident ira plus vite. En attendant, les quatre gestes qui ne cassent rien restent : copie, panel, ticket hébergeur, test visiteur. Vous pouvez déclarer le dossier sans envoyer d'accès tout de suite.