WordPress · 10 min · publié le 8 mars 2025 · mis à jour le 26 octobre 2025

JavaScript injecté dans wp_options : le pied de page propre, le HTML sale

Le pied de page du thème est propre, le HTML public contient un script : il sort de `theme_mods`, d'un widget, d'une option de cache ou d'un champ SEO. Un grep en base trouve ce que l'éditeur de fichiers ne montre pas. Sauvegardez la ligne avant de la vider.

Réponse directe

theme_mods, widget_text, des options de cache. Un grep en base trouve ce que le thème ne montre pas. Sauvegardez avant de vider une option.

wp_options javascript injection options wordpress script en base wp

Pourquoi le thème « officiel » ne suffit pas

Comparer `footer.php` au zip du thème est nécessaire et insuffisant. WordPress assemble le HTML à partir de la base : mods du customizer, widgets, extra CSS, extra JS de plugins. Un attaquant qui a un admin, une faille d'option, ou une injection SQL n'a pas besoin de toucher le disque. Le fichier reste signé « propre » par tous les scanners.

C'est le cas le plus déroutant pour les gens qui ont « déjà tout réinstallé ». Ils voient un script dans « Afficher le code source » et rien dans le FTP. Le script est une chaîne dans `wp_options` (ou une table d'extension), parfois sérialisée, parfois en base64 dans une valeur anodine.

Avant de chercher, copiez la base. Puis travaillez sur le dump, pas à coups de « vider le cache » en boucle. Le cache ment ; le dump, moins.

Un thème enfant propre avec des `theme_mods` sales donne exactement ce tableau. Inspectez les deux couches. Voir thème enfant infecté si le fichier, lui, a bougé.

theme_mods, widgets et customizer

`theme_mods_nomdutheme` contient des URLs d'images, du CSS additionnel, parfois du HTML de footer « builder ». Un widget Texte ou HTML personnalisé est le véhicule le plus simple : un `<script src="https://cdn-inconnu/...">` au milieu des horaires. `sidebars_widgets` dit seulement quel widget est où ; le payload est dans `widget_text` ou `widget_custom_html`.

Le customizer « CSS additionnel » et les plugins qui ajoutent un champ « code dans le head » (headers & footers, insert headers) sont des options dédiées. Elles existent pour vous ; l'attaquant les utilise pour la même raison. Listez ces plugins, ouvrez leurs options, ne vous fiez pas à l'absence de menu si le plugin a été masqué.

Les menus peuvent porter un lien externe. Moins souvent un script. Regardez quand même : un item « Support » vers un faux Microsoft, c'est du phishing, pas du skimming, et ça se traite tout de suite.

  • `theme_mods_*` : extra CSS / HTML inattendu.
  • `widget_text` / `widget_custom_html`.
  • Plugins « insert headers and footers » et équivalents.

Options de cache, CDN et « performance »

LiteSpeed Cache, WP Super Cache, WP Rocket, des plugins « CDN » : ils stockent des extraits HTML, des règles, parfois un champ « JS combiné ». Un payload collé là est resservi même après que vous ayez nettoyé le widget. Videz les caches après chaque correction — et lisez les options du plugin de cache, pas seulement le dossier `cache/`.

Un champ « exclude » ou « HTML minify » rarement ouvert est un bon cachette. Grep le dump sur le nom du plugin et sur `<script`. Si un objet sérialisé casse quand vous l'éditez, restaurez la ligne depuis l'export et passez la main : une sérialisation PHP trop courte met le site en erreur 500.

Champs SEO, pixels et Tag Manager

Yoast, Rank Math, SEOPress : verification Google, extra head, fil d'Ariane. Un conteneur GTM que vous n'avez pas créé, un pixel Meta ajouté, un Hotjar inconnu : le HTML a l'air « marketing », l'exfiltration part. Vérifiez les IDs. Un GTM détourné charge ce que le pirate veut, y compris un skimmer, sans que le thème ne change. Voir les articles sur Tag Manager et pixel injecté si l'ID n'est pas le vôtre.

Les champs « code de conversion » des plugins de pub et des thèmes marketplace sont la même famille. Un thème nulled arrive parfois déjà avec un script « stats ». Ce n'est pas une option que vous avez remplie ; c'est un cadeau. Comparez à une install neuve du thème officiel.

Grep utile, UPDATE dangereux

Dans le dump : `<script`, `javascript:`, `fromCharCode`, `eval(`, `atob(`, `String.fromCharCode`, des domaines hors votre liste (cdn, stats, « jquery » hébergé nulle part). Exportez chaque hit (`option_id`, `option_name`, extrait). Puis décidez ligne par ligne.

Un `UPDATE wp_options SET option_value='' WHERE option_name LIKE '%cache%'` est une catastrophe. Vous cassez le site et vous perdez le payload. Même prudence sur `theme_mods` : vider l'option remet le customizer à zéro (logo, couleurs, menus parfois). Retirez le morceau HTML/JS, ne tronquez pas la valeur entière si le reste est légitime — ou restaurez depuis un dump propre lu, pas depuis « il y a trois mois » au hasard.

WP-CLI `wp option get` / `wp option patch` (selon versions) évite phpMyAdmin. Restez sur une copie si vous tâtonnez. La production n'est pas un bac à sable.

Sauvegardez la ligne avant chaque modification. C'est la phrase que les dossiers « j'ai tout perdu » n'ont pas suivie.

Ce que le visiteur reçoit vraiment

Ouvrez le site en navigation privée, mobile, depuis un résultat Google. Affichez le code source, pas seulement l'inspecteur après exécution : certains scripts s'injectent tard. Comparez au HTML de Search Console (inspection d'URL). Un écart, c'est du cloaking ou un cache de bot ; les deux se documentent.

Le cadenas HTTPS ne filtre aucun script. Un CDN « jquery.min.js » dont le chemin n'est pas le vôtre est plus parlant qu'une alerte antivirus. Notez l'URL exacte du script : elle sert au prestataire de paiement si le champ carte est en jeu.

Skimmer : le même tuyau, un autre enjeu

Un script dans les options sur une vitrine, c'est du spam ou du redirect. Sur WooCommerce, c'est potentiellement un copieur de saisie au checkout. Le mode opératoire de recherche est le même ; la décision, non : on isole le paiement le temps du doute. Détail dans skimmer JavaScript WooCommerce.

Ne dites pas « aucune carte n'a été vue » parce que le thème est propre. Le HTML du tunnel décide. Testez le checkout comme un client, lisez le source de cette page-là, pas de la home.

Vider une option sans casser le thème

Si le payload est isolé (un widget HTML), retirez ce widget ou son contenu. Si c'est mélangé à `theme_mods` (JSON / sérialisé), éditez avec un outil qui recalcule la longueur de sérialisation, ou recréez le réglage depuis le customizer après avoir noté logo et couleurs. Recréer tout le thème « pour être sûr » perd le travail de mise en page ; ce n'est utile que si les mods sont irrécupérables.

Les options de plugins abandonnés : désactiver le plugin ne retire pas toujours l'option autoload. L'autoload sale continue de peser et, selon le code restant en mu-plugin, de s'exécuter. Supprimez l'option après export, ou passez `autoload` à `no` une fois le code mort retiré.

Après : caches, CDN, et qui réécrit

Videz plugin de cache, cache hébergeur, CDN (Cloudflare, etc.), object cache. Sinon Google et les clients voient encore l'ancien HTML. Puis refermez l'entrée : admin, API, application passwords, voisin. Une option propre et un compte éditeur fantôme, et le script est recollé le soir.

Relisez le source public 48 h plus tard. Un script qui revient avec un autre domaine de CDN est le même dossier, pas un « nouveau » pirate. Pour un passage en revue des options et du tunnel, créez un compte : rien n'est demandé tant que le prix n'est pas affiché.

  • Payload exporté, puis retiré ligne à ligne.
  • Tous les caches et le CDN invalidés.
  • HTML checkout / home identique à l'attendu.
  • Plus d'écriture admin non nommée.

Questions fréquentes

Wordfence signale « suspicious code in database ». Je clique Repair ?

+
Pas les yeux fermés. Repair peut vider une option légitime (licence, réglages builder) ou laisser un morceau sérialisé cassé. Exportez, lisez, corrigez. Le bouton n'est pas un constat.

Le script n'apparaît que sur mobile. Est-ce encore wp_options ?

+
Oui, parfois : une option ou un plugin sert un HTML différent selon le user-agent. Parfois c'est un fichier ou un `.htaccess`. Contrôlez les trois. La base n'est pas coupable par défaut, elle est candidate.

Puis-je supprimer toutes les options qui contiennent <script> ?

+
Non. Des plugins légitimes y mettent un tracker que vous avez choisi. Filtrez sur les domaines et les scripts que vous ne reconnaissez pas. Un pixel que vous avez posé n'est pas le sujet.

Vider theme_mods remet-il le site en « thème par défaut » ?

+
Ça remet le customizer de ce thème à zéro : logo, couleurs, parfois menus. Le thème reste actif. Notez ou exportez avant. Recréer un logo est moins grave que laisser un skimmer ; ce n'est pas une raison pour tronquer sans copie.

Un extra CSS du customizer peut-il exfiltrer des cartes ?

+
Le CSS seul, rarement. Un champ « CSS » qui accepte du HTML/JS, ou un plugin qui imprime le champ sans filtre, oui. Lisez ce qui est réellement servi dans le `<head>` et avant `</body>` sur le checkout.
À lire ensuite
Base WordPress injectée Skimmer WooCommerce Cache qui ressert le malware WordPress piraté Google Tag Manager détourné Déclarer mon site