Enlever virus WordPress : configurer un WAF et limiter l’exécution PHP
Un site WordPress infecté, ce n’est pas seulement une page qui s’affiche mal. C’est souvent une chaîne d’incidents. Un plugin ou un thème compromis ouvre une porte, un attaquant injecte un script dans le code, puis le serveur sert le contenu malveillant aux visiteurs. Et tant que la porte reste ouverte, vous pouvez réinstaller WordPress dix fois, le problème revient.
Quand on parle d’enlever virus WordPress, on pense évidemment au nettoyage. Mais le nettoyage seul, surtout sur un parc de sites ou une installation qui bouge en continu, n’est pas une stratégie. La bonne approche consiste presque toujours à combiner trois couches: une élimination du contenu malveillant, une mise en barrière à l’entrée (WAF), et une réduction stricte de ce que le serveur a le droit d’exécuter (limiter l’exécution PHP). C’est cette logique en couches qui évite le scénario le plus pénible: “c’est propre”, puis “ça a re-sauté” une semaine après.
Les symptômes qui doivent faire agir tout de suite
Il y a des infections discrètes, et d’autres qui deviennent visibles en quelques minutes. Les signes fréquents ne sont pas tous spectaculaires, mais ils ont un point commun: ils ne ressemblent pas à un bug logiciel banal.
Vous pouvez voir apparaître des redirections vers des pages de spam, des mots-clés que vous n’avez jamais publiés, des formulaires qui “réagissent” sans raison, ou des pages qui changent sans mise à jour WordPress et sans modification de thème. Parfois, c’est pire: le site passe en erreur 500, https://gardewp.fr/nettoyage-malware-wordpress/ puis revient, puis retombe, comme si un script malveillant relançait des actions en boucle.
Sur le plan technique, je regarde d’abord deux choses: l’accès serveur (logs) et les changements côté fichiers. Une infection laisse presque toujours une trace: un téléchargement suspect, un binaire créé, des fichiers récemment modifiés dans des dossiers inattendus, ou des requêtes HTTP avec des patterns bizarres. Si vous n’avez pas accès aux logs, vous pouvez quand même observer les fichiers et la base, mais vous manquerez souvent le moment exact où la compromission a commencé.
Un cadre de réaction réaliste, sans se disperser
Avant de configurer quoi que ce soit, je recommande de figer le contexte. La tentation est grande de “nettoyer vite” et de “voir après”. Sauf qu’en pratique, dès qu’on touche aux fichiers, on peut aussi casser des traces utiles, ou compliquer la comparaison entre “avant” et “après”.
Dans une intervention que j’ai menée sur un site de vitrine, le premier réflexe a été d’installer deux plugins de sécurité “au cas où”. Résultat: les plugins ont ajouté leurs propres fichiers, et la chronologie est devenue floue. On a gagné en confort de scan, on a perdu en clarté sur l’origine. On a finalement dû repartir sur une archive et recommencer. Moralité: nettoyez, oui, mais nettoyez en gardant une discipline sur la preuve.
L’approche la plus robuste consiste à préparer un plan de travail et à conserver un état de référence. Si vous pouvez, faites une copie complète des fichiers WordPress et de la base de données. Ensuite seulement, attaquez le nettoyage.
Voici le minimum utile, en une courte check-list, pour éviter les erreurs classiques:
Vérifier l’intégrité via scans (fichiers et base), mais conserver une archive de travail. Identifier les fichiers nouvellement créés ou modifiés (dates, tailles, chemins hors logique). Couper l’accès temporairement si nécessaire (maintenance) pour réduire la propagation. Remplacer les composants WordPress par des versions saines (cœur, thèmes, plugins si suspects). Refaire les clés et mots de passe (utilisateurs WordPress, FTP, base, hébergement) après le nettoyage.
Nettoyage: ce qu’il faut regarder, sans tomber dans le “tout supprimer”
Le nettoyage, ce n’est pas seulement supprimer un fichier visiblement malveillant. Les infections modernes sont parfois en plusieurs étapes: un script place un webshell, puis modifie des hooks, puis manipule des options dans la base. Dans ce genre de cas, supprimer un seul fichier ne suffit pas, parce que le “mécanisme” reste en place ailleurs.
La base de données est souvent un bon indicateur. Sur WordPress, cherchez des modifications inattendues dans des champs d’options, dans des valeurs liées à des paramètres de redirection, ou dans du contenu injecté. Pour les fichiers, regardez particulièrement les emplacements qui ne devraient pas contenir de logique PHP “sale”: dossiers “uploads” (si des fichiers PHP y apparaissent), caches, thèmes qui n’ont pas bougé, et répertoires temporaires.
Le cœur de la logique, quand vous cherchez à enlever virus WordPress, c’est de trier:
Ce qui doit être exécuté (et seulement ce qui doit l’être). Ce qui doit être stocké (images, PDF, médias). Ce qui ne devrait jamais être là (fichiers PHP hors des zones attendues).
Quand vous trouvez un fichier suspect, évitez de “l’effacer et prier”. Remplacez aussi les composants dont il dépend. Par exemple, un fichier injecté peut appeler une fonction depuis un plugin compromis. Si vous supprimez uniquement le fichier déclencheur, mais que le plugin reste implanté, vous aurez un retour.
Mettre une barrière en amont: configurer un WAF
Un WAF ne remplace pas le nettoyage. Il réduit la probabilité de réinfection et limite l’impact, surtout si l’attaque vient de tentatives d’exploitation automatisées. En pratique, un WAF vous apporte deux bénéfices concrets: une inspection des requêtes et des règles qui bloquent des patterns connus (injections, scans, payloads typiques).
Le point important, c’est que le WAF n’est utile que s’il est correctement placé. Si votre WAF est “actif” mais ne voit pas le trafic, vous payez une couche de plus pour rien. Idéalement, faites en sorte qu’il soit au plus proche du flux entrant, au niveau DNS ou proxy, ou au niveau reverse proxy sur le serveur.
Il y a plusieurs options selon votre hébergement. Sur beaucoup de sites, le duo le plus courant est un WAF côté CDN (par exemple Cloudflare) et/ou un WAF applicatif via un plugin de sécurité. L’un ne remplace pas l’autre, et les erreurs d’interprétation sont possibles, surtout si vous avez un site avec du cache dynamique et beaucoup d’URL générées.
Exemple de configuration WAF orientée WordPress
Sans entrer dans une liste interminable de menus, je pense à trois objectifs: bloquer les attaques répétitives, réduire les vecteurs d’injection, et éviter d’handicaper le fonctionnement normal.
Si vous utilisez un WAF type Cloudflare, commencez par activer un niveau de protection raisonnable, puis observez les logs. Trop dur dès le premier jour, et vous allez casser l’admin, les formulaires, ou des plugins de captcha. Trop permissif, et vous n’aurez pas l’effet recherché.
Sur un site WordPress, il y a souvent des points à ajuster:
Les endpoints d’administration (wp-login, wp-admin, wp-json) qui reçoivent beaucoup de scans. Les téléchargements et paramètres qui peuvent contenir des payloads (fichiers, requêtes). Les requêtes qui ressemblent à de l’exploration de répertoires ou à des tentatives SQL.
Si vous constatez des faux positifs, je privilégie une approche “observer puis ajuster”, plutôt que baisser tout le niveau. Par exemple, vous pouvez créer une règle spécifique pour autoriser un chemin si vous avez une intégration connue, tout en gardant des protections générales.
Dans un incident sur un WordPress utilisé pour des demandes de devis, le WAF a commencé à bloquer les requêtes vers admin-ajax.php pendant un pic. Le site ne “cassait” pas complètement, mais les formulaires ne répondaient plus. On a corrigé en constatant le pattern, puis en ajoutant une exception ciblée sur les chemins réellement nécessaires, au lieu de désactiver la protection.
Réglages WAF essentiels (et ce que j’ai appris à vérifier)
Voici les réglages que je juge vraiment structurants, ceux qui font la différence entre une protection théorique et une protection opérationnelle:
Vérifier que la protection WAF est bien au niveau du trafic entrant (test depuis l’extérieur). Contrôler les règles sur admin et les endpoints API WordPress (wp-json, admin-ajax.php). Consulter les logs de blocage sur 24 à 72 heures après activation pour repérer les faux positifs. Activer des protections contre les injections et requêtes malformées, sans pousser immédiatement au niveau le plus agressif. Mettre en place une règle de rate limiting sur les tentatives de connexion et de scan.
En cas de doute, retenez ce principe: un WAF doit vous empêcher de tomber, pas vous empêcher de travailler. Si vous bloquez un endpoint légitime, le site devient fragile et personne ne comprend pourquoi.
Limiter l’exécution PHP: réduire la surface d’attaque au maximum
Le WAF fait un travail d’inspection. Mais si un attaquant parvient quand même à déposer un fichier, la question devient: ce fichier peut-il s’exécuter? Sur WordPress, limiter l’exécution PHP est une des mesures les plus efficaces, parce qu’elle casse un schéma fréquent des infections: l’upload d’un script PHP, puis son exécution via une URL accessible.
Concrètement, vous voulez empêcher l’exécution PHP dans les dossiers où vous stockez du contenu. Le cas classique est le dossier uploads: on y met des images, PDF, documents. Si un fichier PHP y est exécuté, vous avez une porte.
L’approche dépend du serveur. Sur Apache, on s’appuie sur .htaccess. Sur Nginx, c’est côté configuration du serveur.
Apache (avec WordPress en mod_php ou proxy Apache)
Dans votre .htaccess, le but est de désactiver l’exécution PHP dans le dossier uploads. La façon exacte dépend des directives PHP gérées par le serveur, mais l’idée reste la même: retirer le traitement PHP dans ce répertoire.
Voici un exemple souvent utilisé, à adapter si votre hébergement gère PHP autrement:
# Empêcher l'exécution PHP dans les uploads Php_flag engine off Require all denied # Variante avec FilesMatch plus ciblée si nécessaire # # SetHandler None #
À la réalité du terrain, je conseille de vérifier votre pile. Certains hébergeurs utilisent php-fpm et des configurations différentes, auquel cas php_flag engine off ne fera pas ce que vous attendez. Dans ce cas, on s’oriente vers une règle de type:
SetHandler "none"
Avant de déployer, testez sur un environnement de staging ou au minimum via un fichier PHP de test placé dans uploads, supprimé juste après vérification. Le test ne doit pas rester, et il ne doit pas être accessible publiquement longtemps.
Nginx (avec php-fpm)
Sur Nginx, la logique est généralement plus directe: vous pouvez empêcher l’accès au PHP dans uploads et rediriger vers une page d’erreur ou renvoyer 404. Exemple typique:
Location ~* ^/wp-content/uploads/.*\.php$ Return 404;
Si votre structure a des sous-dossiers particuliers ou si vous servez WordPress via un alias, adaptez le chemin. L’objectif est simple: aucune interprétation PHP dans ces répertoires.
Le piège des “fichiers PHP dans uploads”
Quand on limite l’exécution PHP, on découvre parfois qu’il y a déjà eu des dépôts. Dans ce cas, vous ne limitez pas seulement pour l’avenir, vous neutralisez aussi ce qui est présent.
Mais attention aux effets de bord. Un site peut, par inadvertance, contenir un fichier .php légitime dans un dossier que vous pensiez “statique”, par exemple un script de formulaire hérité, ou un petit utilitaire oublié. La mesure est saine du point de vue sécurité, mais elle peut casser un fonctionnement interne. D’où l’importance de vérifier l’existant avant de verrouiller à fond.
Dans mon expérience, la majorité des sites n’ont pas besoin de PHP dans uploads. Quand c’est le cas, c’est souvent déjà un signal d’une pratique non recommandée, ou d’un bricolage ancien. On peut alors migrer la logique vers un emplacement prévu, ou reconfigurer l’usage du serveur.
Verrouiller ce qui entoure la PHP, pas seulement la PHP
Limiter l’exécution PHP, c’est puissant, mais ça ne suffit pas. Un attaquant peut tenter d’exploiter des failles dans des plugins, d’abuser des formulaires, ou de modifier des fichiers ailleurs. Les couches complémentaires aident à stabiliser après nettoyage.
Je vérifie systématiquement:
Les droits d’écriture sur les répertoires sensibles (wp-content et surtout les zones où un dépôt est possible). Les utilisateurs WordPress: mots de passe forts, suppression des comptes inconnus, et revalidation des rôles. La mise à jour des plugins et thèmes, ou leur suppression si inutiles. La désactivation temporaire des plugins suspects pendant la phase de durcissement.
Le point délicat, c’est le trade-off opérationnel. Si vous durcissez trop vite, et que vous coupez un plugin légitime, vous perdez du temps à diagnostiquer. C’est pour cela que je préfère les changements “sécurisés” par tests: un verrou à la fois, un test de fonctionnement, puis on passe au suivant.
Le scénario “on a nettoyé, mais ça revient”: diagnostic rapide
Quand un site réinfecte, la cause la plus fréquente n’est pas une erreur de suppression. C’est l’accès qui reste possible. Un mot de passe faible, un compte admin oublié, un plugin rarement mis à jour, ou une intégration FTP encore active.
Le bon réflexe est de regarder les logs d’accès (WAF et serveur). Si vous voyez encore des tentatives vers des endpoints d’exploitation, votre WAF n’a peut-être pas le niveau de couverture attendu, ou bien vos règles ciblées ne sont pas assez actives. Si les tentatives ont disparu mais que des fichiers reviennent, c’est plutôt un accès interne compromis (identifiants, API, scripts automatisés).
Un autre indice: les dates de modification. Si de nouveaux fichiers apparaissent dans des dossiers inattendus le même jour que des tentatives d’upload, vous avez une corrélation claire. Si, au contraire, les modifications arrivent sans pattern, il faut penser à des tâches programmées, à des webhooks, ou à des intégrations qui retouchent le contenu.
Plan d’action concret après une infection (nettoyage plus WAF plus verrouillage)
Vous pouvez traiter cela comme un chantier, pas comme une chasse au “fichier unique”. Voilà une séquence qui fonctionne bien en pratique, parce qu’elle fait progresser chaque couche.
Commencez par stabiliser le site, idéalement via un mode maintenance si le trafic est agressif. Ensuite, nettoyez en remplaçant les éléments connus (cœur WordPress et composants) par des versions saines. Puis, vérifiez les utilisateurs, les options et les fichiers récemment modifiés. Une fois l’état “propre” obtenu, vous mettez le WAF en place, ou vous augmentez la protection si elle était absente.

Enfin, vous verrouillez l’exécution PHP dans les zones de stockage. Si vous ne faites que deux choses, je dirais: WAF pour réduire le flux d’attaques, et blocage PHP dans uploads pour couper la mécanique d’upload-exécution.
Après ces changements, surveillez 24 à 72 heures. C’est la fenêtre où vous voyez le plus de répétitions, et où vous identifiez les faux positifs. Si votre site est “fragile” à cause d’un blocage trop strict, vous le verrez vite. Si tout roule, vous aurez un signal positif: le nettoyage a tenu, et le durcissement ne gêne pas le fonctionnement.
Attention aux faux positifs et aux effets sur l’exploitation légitime
Limiter l’exécution PHP et renforcer le WAF ont un point commun: ils peuvent casser des choses qui fonctionnaient par accident.
Exemples typiques:
Un plugin d’upload qui s’appuie sur un script PHP exécuté depuis un dossier que vous avez ensuite verrouillé. Un script interne qui passe par admin-ajax.php avec des paramètres que le WAF juge “toxiques”. Un thème ou un code personnalisé qui utilise des endpoints non standards.
La règle que j’applique est simple. Je préfère construire des exceptions ciblées plutôt que désactiver complètement. Et je garde une trace de ce que j’ai autorisé. Deux semaines plus tard, si quelque chose redevient suspect, vous saurez pourquoi un accès a été permis.
Mesurer le risque et le rendre gérable
Il y a une tentation, après une infection, de tout verrouiller immédiatement. Parfois c’est nécessaire, mais dans la majorité des cas, ce qui marche le mieux est une démarche graduelle.
Vous pouvez viser une combinaison qui rend la réinfection difficile:
WAF pour réduire les tentatives automatisées et les payloads courants. Désactivation de l’exécution PHP dans les répertoires de contenu. Mises à jour régulières et suppression de ce qui n’est pas utilisé. Comptes admin limités et authentification robuste.
Cette stratégie n’offre pas une immunité absolue. Une vulnérabilité inconnue, un identifiant compromis, ou une chaîne d’accès mal gérée peut toujours ouvrir une brèche. Mais elle change la probabilité. Elle transforme “un script peut s’exécuter et rester discret” en “l’attaque est bloquée, ou l’attaque échoue sans impact durable”.
Ce que je ferais la prochaine fois sur un site WordPress à risque
Si je devais résumer l’approche à garder en tête, ce serait celle-ci: après avoir enlevé virus WordPress, vous devez traiter le site comme un système exposé en permanence, pas comme un projet ponctuel.
On évite les zones grises. On réduit les permissions. On met un WAF pour absorber le bruit des attaques. Et on empêche l’exécution PHP là où elle n’a aucune raison d’exister.
Quand je vois un site qui a traversé ce genre d’incident et qui reste stable après durcissement, on reconnaît un vrai retour d’expérience. Ce n’est pas juste “on a nettoyé”. C’est “on a rendu la réinfection plus chère, plus difficile, plus visible”.
Si vous me donnez votre contexte (hébergement Apache ou Nginx, présence ou non d’un CDN/WAF, méthode PHP-FPM ou mod_php, et les dossiers que vous utilisez pour les uploads), je peux vous proposer une configuration de verrouillage PHP plus précise, avec les points à tester pour éviter de casser un plugin ou un flux d’upload.
