Quand un WordPress se fait compromettre, le premier réflexe est souvent de “nettoyer site WordPress infecté” et de restaurer une sauvegarde. C’est nécessaire, mais insuffisant. Les campagnes actuelles ne visent pas seulement un fichier “magique” à supprimer. Elles s’appuient sur des habitudes de construction de thème et de scripts, sur des chemins d’exécution trop permissifs, et sur des zones où l’on copie du code sans vraiment comprendre d’où il vient.
Dans cet article, je vais parler d’un angle précis, celui du thème et des scripts. Parce que dans beaucoup d’incidents, le point faible n’est pas WordPress core, mais ce qui l’entoure: fichiers du thème, https://gardewp.fr/nettoyage-malware-wordpress/ snippets chargés par le thème, scripts ajoutés via un éditeur, plugins qui inyectent du code dans les templates, ou encore un “petit script” pour le tracking devenu un cheval de Troie.
Les signes qui pointent vers le thème, pas seulement vers “le site entier”
Avant de toucher au thème, il faut être capable de dire, avec un minimum de méthode, où ça fuit. Les infections produisent parfois des symptômes très bruyants, mais bien souvent c’est plus subtil. J’ai déjà vu des sites qui continuaient à fonctionner normalement côté navigation, mais qui injectaient du contenu uniquement sur certaines pages, ou uniquement pour certains User-Agent, ou encore à des heures spécifiques.
Dans un incident orienté thème, on retrouve souvent des indices comme ceux-ci, décrits en pratique sur des chantiers de nettoyage:
- des redirections vers des domaines inconnus apparaissent après une mise à jour du thème, après ajout de “code custom”, ou après modification de fonctions; une “porte dérobée” s’active sur une URL précise, par exemple /wp-content/themes/nom-theme/index.php ou un fichier inclus depuis functions.php; des scripts se chargent depuis un domaine externe, mais uniquement quand on ouvre une page particulière, par exemple la page produit ou la page de contact; des fichiers nouveaux apparaissent dans le thème, avec des noms peu évocateurs, parfois en double extension ou dans des sous-dossiers.
L’idée n’est pas d’attendre le pire. C’est de repérer tôt les motifs récurrents, car ils orientent le type de durcissement à appliquer ensuite. Le thème est un bon candidat: il est très fréquemment modifié, il exécute du PHP à chaque chargement, et il manipule des données (templates, shortcodes, hooks) qui peuvent devenir des portes d’entrée.

Triage: agir vite, mais sans casser davantage
Quand on soupçonne un compromis, le pire scénario est celui où l’on nettoie à l’aveugle et on supprime “ce qui semble suspect”, puis on restaure des morceaux partiels, ce qui laisse une infection en place ou crée une incohérence de fichiers. On perd alors le fil d’analyse.
Voici une approche pragmatique, qui marche bien pour un “nettoyage thème + scripts” sans tomber dans le théâtre.
- Mettre le site hors ligne ou le protéger par une barrière temporaire (mode maintenance, ou blocage via règles serveur) pour éviter l’exécution en boucle et la fuite de données. Lister les fichiers modifiés récemment, et comparer avec une version connue saine (tag de thème, commit, copie fraîche depuis le dépôt ou la release). Identifier les points d’exécution dans le thème: functions.php, templates, fichiers inclus par hooks, et tout code qui touche à wp_enqueue_script, wp_add_inline_script, require, include, file_get_contents, ou des balises directes. Couper temporairement les “imports de code” si le thème charge des snippets externes ou des assets conditionnels, le temps de confirmer l’origine. Préparer une copie forensique des fichiers du thème et des scripts liés, avant modification, pour garder une trace.
Ce tri ne remplace pas une analyse de logs, mais il permet de commencer proprement. Dans plusieurs cas, les fichiers du thème contiennent du code “de faible volume” mais très ciblé. Par exemple, un functions.php qui n’a pas l’air énorme, mais qui ajoute un hook au chargement de page et injecte une charge utile plus bas dans la chaîne.
Ce qui rend un thème dangereux quand il est compromis
Un thème peut être “infusé” de code malveillant de plusieurs manières. Les plus fréquentes ne nécessitent pas une complexité extrême, juste une insertion au bon endroit.
1) L’abus des hooks dans functions.php
Le cœur du problème, ce sont les hooks. Un thème peut enregistrer des actions et des filtres qui s’exécutent en quasi continu. Si un attaquant injecte une fonction qui s’enregistre sur un hook courant, le code malveillant tourne à chaque requête, ou à des moments clés comme le rendu du footer, l’enqueue des scripts, ou la génération de certaines pages.
Ce n’est pas forcément “du malware complet”. Parfois c’est un bloc PHP qui:
- examine des variables de requête, dont $_SERVER['REQUEST_URI']; vérifie un en-tête ou un paramètre pour éviter la détection; charge dynamiquement un script à partir d’une URL distante; modifie discrètement le HTML (ajout de balises, injection de tags, ajout d’un iframe).
La gravité vient de la combinaison: le thème ne se contente pas de présenter, il exécute.
2) Les inclusions dynamiques et les fichiers “fantômes”
On rencontre souvent des patterns du type include($_GET['...']), require sur une chaîne construite, ou un chargement d’un fichier sous un nom bizarre. Même si ces lignes ne sont pas présentes au départ, des variantes se glissent après compromission.
Autre variante: de nouveaux fichiers apparaissent, parfois avec des noms qui imitent des fichiers de build, ou des répertoires ajoutés dans le thème. Ensuite, un petit morceau de code dans un template “appelle” le fichier.
Dans une telle configuration, supprimer un seul fichier injecté n’est pas suffisant. Il faut remonter à la chaîne complète: qui inclut qui, à quel moment, et avec quels paramètres.
3) L’abus de l’enqueue de scripts
WordPress facilite l’ajout de scripts via wp_enqueue_script et ses cousins. Cela devient une surface d’attaque quand:
- le thème charge un script depuis une URL externe non attendue; le thème ajoute du inline JS à partir d’une source non vérifiée; le thème construit des URLs à partir de données de requête; des scripts sont chargés uniquement dans certaines conditions.
Je me souviens d’un cas où le site avait l’air propre dans les pages d’accueil, mais sur une page catalogue, on voyait un script additionnel. En cherchant côté thème, on a découvert un wp_footer conditionnel, déclenché selon une partie de l’URL. Le script externe faisait du tracking, mais il y avait aussi une redirection en arrière-plan pour une fraction des visiteurs. Le code malveillant n’était pas “massif”. C’est justement ce qui le rendait difficile à détecter.
Restaurer une base saine, puis reconstruire proprement
Après tri, la tentation est grande de modifier “au cas par cas”. Sur les thèmes, je recommande une stratégie plus structurée:
1) Obtenir une version saine du thème. 2) Réappliquer une couche de personnalisation contrôlée. 3) Vérifier que les hooks et l’enqueue de scripts sont cohérents avec votre besoin réel.
Si votre thème est acheté ou basé sur un framework, la récupération d’une release depuis la source officielle (ou votre repository interne) devient un atout. Sans cela, vous risquez d’avoir un thème “corrigé” mais encore contaminé, parce que vous n’avez pas réellement comparé.

Une restauration saine ne consiste pas seulement à supprimer les lignes suspectes. Elle consiste aussi à remettre en état les structures normales du thème, puis à réintroduire uniquement ce que vous savez être utile.
Auditer le thème sans noyer l’analyse
Une fois le code revenu à une base saine, vous devez vérifier que rien n’a “resurgi” pendant le retour. Dans beaucoup d’incidents, le vecteur n’est pas seulement le thème au sens large, c’est la façon dont le thème charge et combine les scripts.
Voici les points que je vérifie systématiquement quand le thème est impliqué:
- Présence de fonctions dans functions.php qui appellent des fonctions de lecture de fichiers ou de réseau (à minima: file_get_contents, curl_exec, fopen sur des URL, fsockopen). Même si votre thème n’utilise pas ces fonctions, il n’y a aucune raison d’en introduire. Utilisation d’eval, base64_decode et concaténations de chaînes. Les charges utiles se cachent souvent dans ce type de logique. Enqueue conditionnel sur des URL, sur des User-Agent, ou selon des cookies. C’est un indicateur fort. require et include avec des chemins dynamiques. Les inclusions doivent rester déterministes. Manipulation directe de la sortie HTML dans les hooks de rendu. Le thème peut le faire, mais si ça déclenche des redirections ou des scripts inattendus, c’est un drapeau rouge.
L’objectif n’est pas de rendre le thème “stérile”, mais de s’assurer que chaque point d’exécution correspond à une décision de design. Un code custom pour un formulaire n’a pas besoin de charger un script distant.
Durcir le thème pour éviter la récidive
Nettoyer un incident, c’est une opération de chirurgie. Renforcer, c’est mettre des gardes-fous. Le durcissement du thème et des scripts vise trois choses: réduire la surface d’exécution du PHP, limiter la manière dont des scripts externes s’injectent, et empêcher qu’un attaquant ne remplace silencieusement des fichiers.
1) Séparer le custom du thème “mère”
La pratique la plus rentable, surtout si vous utilisez un thème éditable, est de séparer vos modifications de la base de thème.
Concrètement, au lieu d’insérer du code custom directement dans les fichiers du thème à chaque fois que vous ajustez une page, vous stabilisez la logique dans un endroit contrôlé. Souvent, cela passe par un plugin “must-use” ou un plugin interne de votre équipe, qui gère le custom proprement. Ainsi, si le thème est compromis, vous limitez la part de logique modifiée à un périmètre plus petit et plus facile à auditer.

C’est aussi un moyen de garder une version saine de thème propre. Si un jour vous devez réinstaller le thème, votre code custom reste au même endroit.
2) Contrôler strictement wp_enqueue_script
Quand un site a été infecté, je considère comme suspect toute enqueue de script qui n’est pas explicitement justifiée. Une bonne sécurité sur les scripts ressemble à une discipline:
- Ne charger que des scripts dont vous connaissez l’origine. Favoriser les fichiers locaux plutôt que des URL externes, si c’est possible. Empêcher l’ajout de scripts inline depuis des sources non vérifiées.
Même sans aller dans une complexité excessive, vous pouvez rendre les comportements “conditionnels” beaucoup plus transparents. Si le thème envoie un script uniquement pour une page spécifique, c’est acceptable, tant que la logique est stable et que la liste des scripts reste maîtrisée.
3) Réduire la dynamique dangereuse
Les infections exploitent la dynamique: logique basée sur des paramètres d’entrée, inclusion de fichiers construits au runtime, extraction de charges codées en base64.
Le durcissement passe par des règles de bon sens:
- Éviter toute lecture de code ou de contenu externe côté PHP dans le thème. Bannir eval et les décodeurs utilisés pour exécuter du contenu. Garder des includes statiques, avec des chemins déterministes.
Dans un projet, on a retiré un “helper” custom qui chargeait un fichier JSON depuis une URL, puis qui reconstruisait des parties de HTML. Le site n’était pas infecté au moment de l’intervention, mais ce morceau rendait les comportements impossibles à sécuriser proprement. Après retrait, l’équipe a remplacé par une récupération serveur contrôlée et une mise en cache maîtrisée. Résultat: moins de surface d’attaque, et une maintenance plus simple.
4) Mettre en place une barrière sur l’intégrité des fichiers
Renforcer sans contrôler l’intégrité est une demi-mesure. Vous voulez détecter rapidement un changement de fichier du thème ou de vos scripts.
Cela peut passer par:
- des checks périodiques d’intégrité (hash) sur les fichiers critiques; des alertes lors de modifications; des permissions qui réduisent la capacité d’écriture.
Je ne donne pas de commande précise ici, car dépendre de votre configuration serveur est déterminant, mais l’idée reste: empêcher l’écrasement silencieux. Un attaquant peut trouver un moyen d’écrire, mais si vous détectez l’anomalie rapidement, vous limitez l’impact.
5) Réviser les autorisations et le flux de déploiement
Un thème qui peut être modifié depuis le panneau d’administration, ou via des plugins d’édition, devient un terrain de chasse. Les attaquants utilisent souvent des mécanismes de mise à jour, d’édition de fichiers, ou des vulnérabilités d’accès.
Même si votre thème est “propre”, la sécurité dépend du processus:
- qui peut éditer le thème, sur quelles machines, avec quel mode de déploiement, et comment vous validez le code avant publication.
Le meilleur garde-fou, c’est le workflow, plus que la punition a posteriori.
Contrôler les scripts injectés via le thème et la sortie HTML
Le moment le plus délicat n’est pas l’enqueue de scripts en soi, c’est l’injection dans la page. On voit parfois des scripts qui passent par wp_footer ou wp_head via des hooks. C’est pratique pour un thème, mais ça devient risqué dès que la logique est mal encadrée.
Pour renforcer, je recommande une règle simple: chaque script ajouté au HTML doit avoir une justification et une source claire. Si vous utilisez un script externe pour un outil d’analytics, vous devez connaître le domaine exact, la version, et la raison d’être. Si vous insérez du contenu inline, vous devez contrôler le contenu, pas seulement “faire confiance”.
Côté rendu, soyez aussi attentif aux transformations de chaînes qui produisent du HTML. Par exemple, si une fonction prend une donnée utilisateur et la renvoie sans échappement cohérent, vous créez une fenêtre d’injection. Ce n’est pas forcément la voie utilisée par un malware, mais ça augmente les possibilités d’exécution non désirée.
Exemple concret de durcissement après nettoyage
Sur un site d’e-commerce, le symptôme était une baisse lente de confiance des visiteurs. Les pages finissaient par charger un script tiers, et dans certains cas des liens “se déclenchaient” depuis le footer, sans action claire de l’utilisateur. Rien n’indiquait un plugin connu. En inspectant le thème, on a identifié un functions.php avec un hook d’injection placé dans un endroit pas habituel.
Le nettoyage a consisté à remplacer le thème par la version saine, puis à réintroduire uniquement:
- les ajustements de structure des templates, un petit bloc de logique pour l’affichage des informations produit, et le paramétrage de scripts nécessaires au tracking.
Ce qui a changé durablement après incident, ce n’est pas juste “le code supprimé”. C’est le fait qu’ils ont cessé d’éditer le thème directement depuis l’interface. Tout ajout a été mis dans un module custom versionné, et chaque script a été listé dans un fichier interne auditable. Résultat: lorsqu’un fichier du thème change, l’équipe le remarque, et surtout elle sait déjà quels scripts doivent exister.
Check rapide: ce que j’exigerais après un incident thème
Si vous voulez une grille de validation, gardez-la courte. Le but est de repérer les signaux forts qui justifient une reprise d’analyse.
- Le thème en production correspond à une version contrôlée, pas une copie “au doigt mouillé”. Aucun fichier ajouté dans le thème n’est non expliqué, chaque nouveau fichier a un propriétaire et un but. functions.php et les templates n’exécutent pas de logique de chargement externe, de décodeurs, ou de redirection conditionnelle inconnue. Les enqueues de scripts correspondent à une liste attendue, avec domaine et dépendance clairs. Les comptes et droits d’administration ont été revus, notamment ceux qui pouvaient modifier le thème.
Erreurs fréquentes après infection, et comment les éviter
On peut faire les bons gestes et quand même rater un point. Les écueils les plus classiques, je les vois souvent:
1) Restaurer la base, puis oublier le code custom. Si le custom se trouvait dans le thème et qu’il contenait le vecteur, vous avez l’impression d’avoir “tout corrigé”, mais en réalité vous avez réintroduit la même logique. 2) Supprimer le script injecté sans comprendre pourquoi il est appelé. Le code peut être ailleurs, dans un hook moins visible ou dans un fichier inclus. 3) Corriger uniquement les fichiers du thème, alors que l’injection provient d’un plugin ou d’un mu-plugin qui modifie le comportement du thème. Dans ce cas, le thème semble “source du problème”, mais il est juste une cible. 4) Négliger la phase de validation. Un site peut paraître normal, mais l’infection peut rester active sur un sous-ensemble de pages ou sur certains visiteurs.
La correction est un chantier. On progresse par couches, en vérifiant à chaque étape.
Sécuriser pour l’avenir: une stratégie de long terme réaliste
Renforcer la sécurité du thème et des scripts ne veut pas dire transformer votre stack en forteresse impossible à maintenir. La réalité, c’est le compromis entre niveau de contrôle et vitesse de déploiement.
Voici les mesures de fond que je privilégie quand l’objectif est la robustesse, pas seulement un “nettoyage” ponctuel:
- Mettre en place un workflow de déploiement versionné pour les modifications de thème, avec validation avant mise en production. Réduire les droits d’écriture, et limiter le nombre de comptes capables de modifier les fichiers. Mettre en place une surveillance d’intégrité sur les fichiers critiques du thème. Réviser systématiquement toute logique d’injection de scripts, surtout via hooks de tête et pied de page. Documenter les scripts attendus et leurs emplacements, pour comparer rapidement en cas d’anomalie.
Même avec une bonne hygiène de développement, les compromissions peuvent survenir via un autre vecteur. Mais lorsque le thème est maîtrisé, vous réduisez fortement la capacité d’un attaquant à maintenir une persistance silencieuse.
Un dernier point sur les “scripts” qui semblent anodins
Dans les incidents, ce qui surprend le plus, c’est le nombre de “petits bouts de code” qui finissent par compter. Un script de tracking mal intégré, un snippet copié depuis un blog, une optimisation “pour améliorer la vitesse” ajoutée sans validation. Individuellement, chacun peut paraître trivial. Ensemble, ils créent un patchwork. Un patchwork, c’est une surface d’attaque et une difficulté d’audit.
La règle que j’applique depuis des années est simple: chaque script ajouté doit être traçable. Traçable vers son besoin, sa source, et son emplacement dans le cycle de rendu. Si vous ne pouvez pas expliquer cela en une minute, vous ne devriez pas le laisser en production, surtout après un épisode de compromission.
Si vous me décrivez votre configuration (thème parent ou enfant, si vous utilisez un builder, la façon dont vous ajoutez du code custom, et ce qui a été observé pendant l’incident), je peux vous proposer une grille d’audit plus ciblée, centrée sur vos hooks et votre logique d’enqueue, sans partir dans des “recettes” génériques.