Site WordPress infecté : vérifier les champs personnalisés compromis

Un site WordPress infecté ne se reconnaît pas uniquement à la page d’accueil qui redirige vers des sites de paris ou à une surcharge brutale du serveur. Dans beaucoup de cas, la compromission est plus discrète. Elle vit dans des zones que l’on oublie facilement, parce qu’elles ne “font pas site” à première vue. Les champs personnalisés, ou custom fields, en font partie. Quand ils sont corrompus, le comportement peut rester cohérent pour l’utilisateur une grande partie du temps, puis se dérégler sur certains contenus, certaines pages, ou certains navigateurs.

Je parle ici de deux réalités différentes mais liées. D’abord, des champs personnalisés injectent du contenu malveillant dans des templates ou des modules. Ensuite, des champs personnalisés servent de boîte noire à des charges utiles qui attendent un contexte précis, par exemple un slug, une catégorie, un rôle utilisateur ou une condition de cache. Résultat: vous pouvez avoir une apparence “propre”, tandis que des bouts de code se déclenchent quelque part derrière.

Ce guide se concentre sur la vérification des champs personnalisés compromis dans WordPress. L’idée n’est pas de “tout supprimer”, mais de comprendre ce que vous avez entre les mains, où il faut chercher, et comment trier le normal du dangereux.

Pourquoi les champs personnalisés deviennent un point d’entrée

Les champs personnalisés sont conçus pour être pratiques. On associe des métadonnées à un article, une page, un média. Un thème ou un plugin peut ensuite lire ces valeurs via l’API WordPress, puis les afficher, les interpréter, ou les transformer. Cette flexibilité, c’est justement ce qui ouvre une porte en cas d’abus.

Une infection qui passe par les champs personnalisés peut prendre plusieurs formes. Parfois, on observe des chaînes “bizarres” dans des valeurs attendues comme du texte court. Plus souvent, on voit des marqueurs d’injection: une balise

L’autre scénario, plus dur à détecter visuellement, consiste à stocker dans les métadonnées des paramètres qui n’ont l’air de rien. Une valeur semble inoffensive jusqu’à ce qu’un template l’utilise pour construire une requête, une URL, ou un morceau de code. Le code malveillant peut être ailleurs, mais la “clé” se trouve dans le custom field.

Dans mes interventions, le schéma le plus frustrant est le suivant: la base paraît en ordre sur le papier, les fichiers du thème et des plugins sont “quasi identiques”, mais quelques articles publient un script à la volée. On finit par découvrir des champs personnalisés ajoutés sur ces contenus précis, et rien ailleurs. Le site WordPress infecté continue de fonctionner, mais avec une bombe à retardement.

Les symptômes qui pointent vers les custom fields

Avant de fouiller, j’ai une règle simple: collecter des indices, même faibles, pour orienter la recherche. Les champs personnalisés laissent souvent des traces par leur effet, pas par leur forme.

Voici des situations fréquentes où ça vaut le coup de vérifier les custom fields:

image

    Certaines pages seulement sont touchées, pas l’ensemble du site. Le contenu malveillant n’apparaît pas dans l’éditeur WordPress, mais bien dans le HTML rendu. Les modifications restent “locales” à un ensemble de pages, par exemple des pages d’articles, des pages “services” ou une catégorie. Vous voyez une injection côté front, mais sans modification claire des templates. Les redirections ou chargements externes ne suivent pas un pattern global, mais semblent liés à un identifiant de contenu, un auteur, ou un type d’objet.

Un détail qui trompe: le plugin de sécurité peut déclarer “aucune menace dans les fichiers”, tandis que des comportements continuent. Si la compromission s’exécute via la base de données, c’est normal qu’un scan fichier ne trouve rien.

Comprendre où WordPress stocke les champs personnalisés

Pour vérifier correctement, il faut savoir ce que vous cherchez. WordPress stocke les métadonnées dans la base, notamment dans les tables wp_postmeta (pour les métas de posts, pages, CPT) et wp_usermeta (pour les métas d’utilisateurs). Pour un contexte “champs personnalisés” classique, on parle surtout de postmeta.

Deux points importants:

La table contient des paires clé/valeur. La “clé” est une chaîne, la “valeur” est une donnée brute au sens base de données, pas forcément du HTML. La même clé peut exister en multiple exemplaires selon les contenus, ce qui vous aide à repérer une valeur qui ne devrait exister que sur quelques éléments.

Quand vous suspectez un custom field compromis, vous cherchez donc des combinaisons anormales de meta_key et meta_value. Ce n’est pas seulement “des scripts dans des champs”, c’est aussi des valeurs inattendues, et parfois des clés qui ne correspondent à aucun usage normal.

Définir le périmètre avant de toucher à la base

Avant toute action, prenez 30 minutes pour cadrer. Je sais, c’est tentant de “chercher et supprimer vite”, mais une mauvaise suppression peut casser un thème, ou pire, effacer des métadonnées légitimes dont dépend un rendu.

Votre périmètre dépend de ce que vous avez observé:

    Si le problème est global, le périmètre est probablement plus large. Si seuls certains contenus sont touchés, commencez par ces contenus. Vous réduisez le risque de casser quelque chose d’innocent.

La première étape pratique est de lister les objets suspects (IDs de pages ou posts, auteurs éventuellement, types de contenu). Ensuite, vous vérifiez dans la base les métadonnées associées à ces objets.

Si vous n’avez pas encore d’IDs, vous pouvez les obtenir via l’URL de l’éditeur côté admin ou via une inspection plus directe en base. L’idée est toujours la même: relier un symptôme visible à un objet WordPress précis.

Vérifier les custom fields qui contiennent du contenu dangereux

Une méthode robuste consiste à rechercher dans la base des patterns typiques de charge malveillante. Vous ne cherchez pas “script” uniquement, vous cherchez des marqueurs plus généraux, comme des tags HTML inattendus, des fonctions PHP ou des chaînes qui ressemblent à des URL étrangères, selon votre contexte.

Concrètement, si vous inspectez wp_postmeta, vous pouvez chercher des occurrences dans meta_value avec des motifs. Selon votre outil, vous ferez une requête SQL, ou vous passerez par un export puis un grep. La prudence vient du fait que certains champs peuvent contenir du HTML attendu, par exemple pour des blocs d’embed légitimes.

Ce qui fait la différence, c’est la cohérence. Un site propre n’a généralement pas des valeurs qui contiennent des scripts dans des meta dont vous n’utilisez pas le HTML brute.

Un exemple vécu: un thème affiche des champs personnalisés “intro” et “body” via des fonctions qui échappent le rendu pour éviter les injections. Pourtant, des pages “intro” contenaient des fragments

Repérer les meta_key anormales ou nouvelles

Une clé de métadonnée (“meta_key”) qui apparaît dans des centaines de lignes, mais ne correspond à aucun usage de votre thème ou de vos plugins, doit déclencher une alerte.

Dans WordPress, les custom fields ont des noms que l’on construit souvent selon un standard interne. Par exemple, des plugins créent des clés du type _prefix_champ. Les thèmes ont aussi leurs propres préfixes. Une infection, elle, peut créer des clés très “génériques” ou au contraire des clés camouflées dans un style normal.

Ce que j’aime faire dans ce genre d’audit, c’est corréler:

    les meta_key présents sur des objets suspects, leur ancienneté approximative (quand c’est possible via logs ou via l’historique de déploiement), et leur fréquence.

Si vous trouvez une clé uniquement sur 3 pages, et que ces 3 pages sont celles où vous voyez l’injection, vous avez un lien solide. Si vous trouvez une clé sur tout le site, ça ressemble davantage à une fonctionnalité ou un plugin, donc la question se déplace vers “ce plugin a-t-il été modifié, ou ses paramètres ont-ils été corrompus”.

Faire attention aux champs “cachés” par des conventions WordPress

Tous les champs personnalisés ne sont pas destinés à être visibles dans l’interface. Certains sont préfixés par un underscore, comme _some_meta. Selon le contexte, ces champs sont souvent masqués à l’éditeur, mais restent lisibles côté thème ou plugin.

Cela veut dire que vous pouvez ne jamais les voir en explorant l’interface, parce que WordPress masque ces champs. Or, côté base, ils peuvent porter le contenu qui déclenche quelque chose.

Quand vous inspectez wp_postmeta, ne vous limitez pas aux clés “visibles”. Vérifiez aussi les clés masquées, surtout si le plugin ou le thème ne rend pas explicitement le champ affiché dans l’interface.

Vérifier aussi les métadonnées d’utilisateurs

Beaucoup d’attaques finissent par transformer un utilisateur en relais. Un utilisateur compromis, avec un rôle admin ou éditeur, peut injecter des champs personnalisés sur des contenus. Dans ce cas, les métadonnées d’utilisateurs (wp_usermeta) peuvent aussi contenir des éléments utiles pour comprendre la contamination.

Deux indices fréquents:

    Des métadonnées de type options internes, ou des champs inattendus associés à un compte récent. Des timestamps qui ne collent pas à votre usage.

Cela dit, ne partez pas du principe que usermeta contient la charge active. Parfois, c’est simplement l’origine du contrôle. Le symptôme reste dans postmeta.

En pratique, je fais souvent les deux, mais pas de la même manière: je commence par relier symptômes à posts ou pages, puis je remonte vers l’auteur, et enfin vers les métadonnées de cet utilisateur.

Traiter la cause, pas seulement le symptôme

Nettoyer les champs compromis est nécessaire, mais ce n’est pas suffisant si le vecteur d’injection reste en place. Sinon, vous nettoyez aujourd’hui, l’attaquant réinjecte demain.

Le vecteur peut être un plugin vulnérable, un thème compromis, un compte avec des droits trop élevés, un cookie de session volé, ou un formulaire mal protégé (par exemple un formulaire qui accepte du HTML sans filtre, puis qui alimente un champ).

J’ai déjà vu des équipes retirer les métadonnées malveillantes sans changer le plugin d’origine. Les champs nettoyés ont tenu quelques heures, puis le même contenu a réapparu, sur les mêmes pages, parce que la logique d’injection n’avait pas été corrigée.

Donc, votre vérification doit être accompagnée d’une démarche de réduction du risque: rotation de mots de passe, durcissement, et suppression des plugins ou thèmes suspects après analyse.

Une procédure pragmatique de vérification (sans casser la prod)

Voici une approche que je recommanderais dans un incident “propre sur les fichiers, étrange sur le rendu”, quand les custom fields semblent impliqués. Elle vise à limiter le risque, et à obtenir un résultat actionnable.

Identifiez les pages touchées et les pages saines. Notez leurs IDs si possible. Copiez la base ou, au minimum, faites un dump de la table concernée (wp_postmeta et idéalement wp_posts) avant toute modification. Recherchez dans wp_postmeta les meta_key et meta_value anormaux pour les seuls IDs suspects. Validez quels templates ou plugins lisent ces meta_key. Si vous ne trouvez pas de lecture, suspectez une charge dormante ou une écriture sans affichage. Nettoyez en priorité les objets suspects, puis contrôlez à nouveau le rendu front, avant d’étendre à l’ensemble.

Cette démarche évite le scénario le plus coûteux: raser trop large et ne pas savoir ensuite ce qui a été cassé, ni si l’infection était réellement contenue.

Filtrer le “faux positif” des custom fields légitimes

Tous les champs personnalisés qui contiennent du HTML ne sont pas malveillants. Un site qui utilise des champs pour stocker des shortcodes, des embeds, ou des blocs HTML légitimes peut avoir des métadonnées avec des tags. Le problème se joue sur ce qui est exécuté, pas sur la simple présence de balises.

Je conseille d’évaluer chaque cas:

    Le champ est-il censé contenir du script ou du style en clair? Est-ce que le champ est imprimé tel quel, ou échappé? La charge déclenche-t-elle un appel réseau, une redirection ou une exécution inline? Le contenu apparaît-il côté rendu là où il ne devrait pas?

Le tri se fait aussi par contexte. Si vous avez un champ custom “gallery_html” alimenté par un outil interne, il peut contenir des tags. Si un champ “subtitle”, supposé être une phrase courte, contient

Un autre piège classique: certains plugins de SEO ou d’optimisation stockent des fragments (comme des balises meta ou des scripts) en base. Si vous supprimez ces valeurs “au hasard”, vous dégradez le site.

C’est pour ça que la phase “relier meta_key à l’usage dans le thème ou plugin” est essentielle. Si votre thème affiche meta_key via une fonction qui échappe, le champ ne devrait pas pouvoir injecter du JavaScript. Si vous voyez quand même du JavaScript dans le rendu, c’est que soit l’échappement n’a pas été fait, soit la valeur est injectée ailleurs, par exemple via un autre chemin de rendu.

Identifier le chemin de rendu: thème, shortcode, hook

Une partie souvent oubliée dans les incidents WordPress, c’est le chemin exact entre “meta_value stocké” et “code exécuté”.

Concrètement, il existe plusieurs voies:

    Le template lit le champ personnalisé puis l’imprime. Un shortcode lit des champs puis les rend. Un hook WordPress déclenche un rendu conditionnel. Un plugin formate le champ et l’injecte dans le HTML.

Quand vous avez un meta_key suspect, prenez 20 minutes pour chercher où ce meta_key est utilisé. Selon votre environnement, vous pouvez:

    faire une recherche dans le code du thème et des plugins, inspecter les hooks en lecture, analyser les fichiers modifiés.

Je ne conseille pas de “nettoyer les valeurs” sans comprendre qui les lit. Sinon, vous risquez de corriger un symptôme tout en laissant un chemin d’injection ouvert.

Quand l’infection utilise des charges “décodées” plutôt que du JavaScript visible

Certains champs personnalisés ne contiennent pas directement du

Indices de ce type:

    valeurs très longues, présence de caractères inhabituels, formats qui ressemblent à du base64, à des concaténations, ou à des modèles de clés.

Dans ce cas, la valeur peut sembler “pas si grave” dans une inspection rapide. Elle devient dangereuse si une fonction du https://gardewp.fr/ thème ou du plugin la décode ou l’insère dans un autre contexte.

Ici, la stratégie “chercher

Exemple de diagnostic en conditions réelles (avec ce que j’ai vu)

Sur un site e-commerce, le problème n’était pas un hack visible sur toute page. Les utilisateurs connectés à un itinéraire de checkout particulier voyaient un script externe se charger. Sur le même thème, le reste du site semblait normal.

En inspectant le HTML rendu, le script était injecté dans un bloc qui, selon le thème, affichait un champ personnalisé associé à certaines pages. Le code responsable lisait un champ page_footer_extra pour construire un bloc. Dans la base, ce champ avait été modifié uniquement sur deux pages liées au parcours.

image

Ce qui a fait gagner du temps, ce n’est pas la recherche de scripts dans toutes les métadonnées. C’est le fait d’avoir identifié précisément le chemin de rendu. Une fois les métadonnées suspectes corrigées, le comportement est revenu à la normale, et en parallèle le plugin à l’origine de la modification a été remplacé et les comptes à risque ont été verrouillés.

Si vous êtes face à un site WordPress infecté, retenez ceci: le diagnostic marche quand il relie l’effet visible, le stockage en base, puis le rendu.

Réparer et durcir après nettoyage

Nettoyer des champs personnalisés compromis est le début. La réparation doit inclure:

    suppression des vecteurs d’écriture non sécurisés, correction des contrôles d’accès, et contrôle des droits des rôles.

J’ai tendance à recommander de revoir les rôles qui peuvent modifier des custom fields, surtout si vous utilisez des plugins d’éditeur ou des builders. Parfois, le problème vient d’un accès trop large, pas d’une faille technique.

Côté technique, vérifiez aussi les protections de WordPress sur la sortie (escaping). Si votre thème affiche des champs personnalisés, il doit utiliser des fonctions adaptées au contexte: échappement HTML, échappement d’attributs, filtrage de liens, etc. Quand ce n’est pas fait, un champ qui “devrait rester texte” devient un vecteur.

Il y a aussi des considérations de performance et de cohérence. Un nettoyage brutal peut casser un cache. Si vous avez un système de cache, invalidez-le après correction pour éviter que du contenu malveillant rendu soit servi depuis un cache.

Points d’attention: sauvegardes, logs et prudence sur la base

Je ne vais pas vous dire de “tout sauvegarder” de façon abstraite. Je vais vous donner ce qui compte en pratique.

    Faites un dump de la table concernée avant suppression ou mise à jour. Ça vous permet de revenir si vous supprimez un champ légitime. Si votre hébergeur propose des backups instantanés, utilisez-les au moment où vous avez confirmé un lien entre meta_key et symptôme. Regardez les logs applicatifs si vous en avez: certaines injections déclenchent des erreurs, ou des appels réseau depuis le serveur.

Un piège fréquent: vouloir exécuter des requêtes SQL directes sans vérifier le préfixe de tables. Sur des installations avec wp_ modifié, c’est une cause classique d’erreur. Faites une vérification rapide avant toute requête.

Construire un modèle mental pour trier: “anormal pour mon site”

Le bon critère n’est pas seulement “c’est du code”. C’est “est-ce normal pour votre site”.

Posez-vous trois questions:

Ce champ existe-t-il déjà sur vos sites sains, ou sur les contenus qui ne posent pas de problème? Est-il lié à une fonctionnalité activement utilisée, ou apparaît-il de nulle part? Le champ est-il rendu de façon sécurisée dans le thème ou le plugin?

Quand vous trouvez un custom field compromis, il y a souvent une rupture de ce modèle. La clé n’a aucun sens, la valeur ne correspond pas à l’usage, ou elle est associée à des pages qui n’ont jamais eu besoin de ce champ.

C’est cette logique qui permet d’éviter le nettoyage destructeur.

Que faire si vous n’arrivez pas à relier meta_key et affichage

Parfois, malgré une inspection minutieuse, vous trouvez des valeurs suspectes, mais vous ne voyez pas où elles sont utilisées.

Deux options réalistes:

    le chemin de rendu est dans un plugin pas encore inspecté, ou dans un fichier généré, ou le champ est écrit par un script compromis, mais n’est pas le composant qui déclenche l’infection immédiatement.

Dans ce cas, élargissez en observant aussi d’autres tables liées aux métadonnées, ou examinez les changements récents dans les plugins et thèmes. Vous pouvez aussi chercher d’autres traces en base: options WordPress, transients, ou scripts stockés dans d’autres colonnes.

Et si vous manquez de temps, la stratégie “contenir puis réinstaller propre” est parfois plus rationnelle. Mais elle doit être préparée, pas faite à l’aveugle, surtout si vous avez des contenus custom très spécifiques.

Maintenir un contrôle régulier après remise en état

Une fois le site propre, un contrôle périodique évite de retomber dans le même scénario. Sans transformer votre maintenance en projet permanent, vous pouvez mettre en place un monitoring léger.

Surveillez les changements de:

    plugins et thèmes installés, comptes à droits élevés, et les valeurs des custom fields les plus sensibles, celles qui sont affichées dans des zones clés du rendu.

Si vous avez un historique des incidents, utilisez-le. Les sites infectés laissent rarement des indices aléatoires. Souvent, la même famille de champs revient, ou la même suite de conditions déclenche l’injection.

L’objectif est simple: que la prochaine fois, vous sachiez plus vite où regarder.

Vérifier les champs personnalisés, ce n’est pas seulement “chercher du script”

Quand on parle de site WordPress infecté, on pense souvent au malware dans les fichiers ou aux redirections globales. Les custom fields compliquent l’histoire, parce qu’ils se placent au carrefour entre la base, la logique de rendu, et les habitudes de maintenance.

En pratique, vérifier les champs personnalisés compromis, c’est:

    relier un symptôme à des objets précis (pages, posts, auteurs), identifier des meta key et metavalue anormaux, comprendre le chemin qui rend ou traite ces valeurs, puis corriger le vecteur de modification.

C’est moins spectaculaire qu’un scan de fichiers, mais souvent plus déterminant. Une valeur malveillante dans une métadonnée peut rester invisible jusqu’au bon contexte. Et quand vous la trouvez, vous gagnez plus que la suppression du contenu: vous obtenez une carte du mécanisme d’attaque, celle qui vous permet de ne plus revivre la même histoire.