Technique et prévention · 9 min · publié le 1 octobre 2026

WordPress en Docker compromis : image, volumes et secrets

Reconstruire l’image ne lave pas un volume wp-content déjà sale. Les secrets dans l’env, les ports publiés et une image wordpress:latest vieille de deux ans restent le cœur du dossier. On inspecte la pile, on ne se contente pas d’un docker compose up --build.

Réponse directe

Rebuild l'image ne suffit pas si le volume wp-content est sale. Inspectez volumes, env, ports publiés. Une image « wordpress:latest » d'il y a deux ans n'est pas un cœur propre.

wordpress docker piraté volume docker malware container wordpress hack

Ce que « rebuild » ne touche pas

docker compose build ou un pull wordpress:latest recrée le système de fichiers de l’image. Les volumes nommés, les bind mounts (./wp-content, ./uploads), le réseau, les fichiers .env : ils survivent. C’est voulu — c’est pour ça que vos articles ne disparaissent pas à chaque déploiement. C’est aussi pour ça que le mu-plugin et le PHP dans uploads reviennent au redémarrage.

Les dossiers que nous reprenons commencent souvent par « on a rebuild, c’est revenu en 48 h ». Le volume était sale, ou le secret FTP/admin n’avait pas changé, ou un cron sur l’hôte réécrivait le bind mount. Le geste a l’air technique. Il n’a pas traité la porte.

Le protocole reste celui d’un WordPress piraté : copier (volume + dump SQL), changer les accès (registry, hôte, panel, admin CMS), trouver l’entrée, nettoyer ou recreer le volume depuis une copie saine, puis seulement rebuild. Inverser, c’est de la mise en scène.

  • Lister les volumes et bind mounts (compose, docker volume ls).
  • Lister les ports publiés (0.0.0.0:80, 3306, 22 dans le container).
  • Lire .env et les environment: du compose — comme un wp-config.
On ne donne pas ici de recettes d’intrusion. On lit votre compose et vos mounts, on compare à une archive connue.

Image, couches et wordpress:latest

`wordpress:latest` d’il y a deux ans n’est plus latest sur votre disque. Le tag a été tiré une fois, le daemon garde l’ancien digest jusqu’au pull. Un cœur PHP 7.4 / WP 5.9 dans un container « officiel » est un cœur obsolète. Rebuild sans pull ne le rajeunit pas.

Une image « wordpress sécurisé » trouvée sur un forum, ou un Dockerfile qui COPY un thème nulled, arrive déjà sale. Comparez le cœur aux zips officiels une fois le volume monté — ou partez d’une image officielle pinnée (wordpress:6.7.2-php8.2-apache, exemple) et remontez thèmes / plugins depuis une source propre.

Les couches intermédiaires (apt, composer) peuvent contenir des binaires que vous n’avez jamais choisis. Après incident, on ne « fait confiance » à une image custom que si vous la construisez depuis un Dockerfile relu, sans ADD d’une URL inconnue. Sinon : image officielle + volumes de contenu inspectés.

Volumes : là où le malware reste

wp-content (plugins, themes, uploads, mu-plugins) est le volume qui revient dans tous les tickets. object-cache.php et advanced-cache.php à la racine de wp-content aussi. Un bind ./ :/var/www/html monte tout le document root : un PHP à la racine survit au rebuild exactement comme sur un mutualisé.

Copiez le volume hors du serveur (docker run --rm -v … ou archive du bind) avant de supprimer. C’est votre preuve et votre filet. Restaurer « le volume d’hier » replace l’infection. On compare, on extrait le métier (uploads légitimes, thème enfant relu), on ne remonte pas le volume tel quel.

Un volume « anonyme » oublié après un down -v mal compris, ou un second compose sur le même projet, laisse deux wp-content. Le container que vous regardez n’est pas celui qui sert le trafic. docker ps, labels, ports : qui écoute vraiment 443.

  • mu-plugins et drop-ins.
  • PHP dans uploads.
  • Thèmes en double (nulled + enfant).
  • Fichiers à la racine si le mount le permet.

Env, secrets et wp-config

WORDPRESS_DB_PASSWORD, clés AUTH, SMTP, identifiants S3, jetons CI : souvent dans .env, parfois dans un secret Docker mal restreint, parfois en clair dans un compose commité. Après incident, tournez-les tous. Un rebuild avec le même .env laisse la base et les API ouvertes.

wp-config.php peut être généré au premier boot puis rester dans le volume. Un include ajouté là survit. Relisez-le comme sur un FTP classique. Les mots de passe d’application WordPress sont dans la base, pas dans l’image : rebuild ignorant.

Le socket Docker exposé au container WordPress (rare, catastrophique) ou un utilisateur hôte trop large : l’attaquant sort du CMS. Cherchez qui a le droit de monter /var/run/docker.sock. Ce n’est pas un réglage « de perf ». Si vous le voyez, déléguez.

Ports publiés et reverse proxy

Publier 3306 ou 22 sur 0.0.0.0 « pour debug » et oublier : l’entrée n’est plus WordPress. Refermez. WordPress derrière Traefik / nginx proxy : les journaux du CMS montrent l’IP du proxy. Configurez les en-têtes réels — voir reverse proxy — pour lire l’entrée, pas pour « hacker ».

Deux stacks (staging et prod) sur le même hôte, mêmes secrets, port staging ouvert au monde : c’est un sous-domaine oublié en version compose. Coupez le publish, htpasswd, ou éteignez staging.

Let’s Encrypt dans un sidecar : un cert mort n’est pas Safe Browsing (cert vs trompeur). Un volume certs partagé trop permissif est un secret de plus à tourner.

La base : un service à part, souvent oublié

Le container mysql/mariadb a son propre volume. Options WordPress injectées, admins fantômes, posts spam : rebuild de l’image wordpress ne les touche pas. Dump, lecture, nettoyage ou restore d’un dump d’avant l’entrée — pas du dump d’hier soir.

WORDPRESS_DB_HOST=db : le réseau compose. Si db est aussi publié, le mot de passe .env vaut un accès distant. Après incident : plus de publish 3306, mot de passe tourné, utilisateurs MySQL listés.

Un second site sur le même serveur MySQL (autre compose, même instance) : voisin de volume SQL. Inventaire des bases, comme un panel mutualisé.

Ordre de travail sur une pile compromise

1) Copie volumes + dump + compose + .env, hors machine. 2) Rotation secrets hôte / registry / DB / CMS / SMTP. 3) Identifier l’entrée (plugin, upload, port, secret). 4) Extraire le métier, jeter le volume CMS sale ou le comparer fichier à fichier. 5) Image officielle pinnée, up. 6) Tests visiteur + inspection, puis listes Google.

Mettre le site en maintenance sur le proxy le temps du volume est légitime. Couper tout le daemon Docker de l’hôte si d’autres projets propres y tournent : mesurez. Un down ciblé vaut mieux qu’un coup de sang.

Les premiers gestes s’appliquent : ne pas déposer un réexamen tant que l’origine (IP du proxy) sert encore le HTML sale. Purgez le cache du proxy.

Réinstaller « un nouveau compose dans un autre dossier » en recopiant le volume wp-content est une migration sale. L’hébergeur suivant (ou le même VPS) reprendra l’infection.

Refermer : tags d’image, volumes propres, moins de privilèges

Pinez les tags (digest si vous pouvez). Plus de latest en prod. Utilisateur non-root dans l’image officielle si votre pile le permet. Pas de docker.sock. Uploads sans exécution PHP (réglage nginx/apache du container ou du proxy).

Sauvegardes : volume + SQL, hors hôte, testées. Une sauvegarde du volume infecté n’est pas un PRA. Moins d’admins WP, 2FA après le nettoyage, pas comme substitut.

Surveillez trente jours : nouveaux fichiers dans le bind, nouveaux admins, CPU du container, hostnames bizarres si vous êtes derrière un wildcard.

Quand la pile dépasse le bricolage

Kubernetes, Swarm, secrets Vault, plusieurs origines : ce n’est plus un article de vitrine. Déléguez si vous ne savez pas dire quel volume est monté sur le container qui reçoit le trafic public.

Une boutique (commandes, cartes tokenisées) dans cette pile : le coût d’une erreur dépasse l’intervention. Constat écrit, accès révocable.

Créer un espace : on demande compose et accès au moment de l’intervention, pas à l’inscription. Le guide WordPress reste la checklist métier une fois dans le volume.

Cas fréquent : VPS Hetzner/OVH, compose copié d’un tutoriel 2022, volume wp-content jamais inspecté, rebuild après alerte Chrome, réinfection le surlendemain.

Questions fréquentes

docker compose down -v est-il un nettoyage ?

+
Cela détruit les volumes nommés du projet : données métier comprises, infection comprise, preuves comprises. Sans copie préalable, vous perdez tout. Ce n’est un outil que si vous avez déjà l’archive et un plan de remontée propre.

Puis-je garder le volume uploads et jeter le reste ?

+
Les uploads sont le lieu classique des PHP déguisés. On les passe au peigne (pas d’exécution PHP, pas d’extensions inattendues), on ne les remonte pas « en confiance » après un kit.

L’image officielle WordPress est-elle forcément saine ?

+
Le cœur livré par l’image officielle à jour, oui comme base. Votre volume et votre .env, non. Une image officielle ancienne sur le daemon, non. Le tag latest local, non.

Faut-il changer l’hôte (le VPS) ?

+
Si l’hôte lui-même est compromis (ssh, docker.sock, rootkit), oui ou alors un durcissement que vous savez faire. Si seul le volume CMS est sale, un nouveau volume + secrets tournés sur le même hôte suffisent souvent. Le doute sur l’hôte se tranche avec les journaux ssh et les ports, pas au feeling.

Un plugin de sécurité dans le container suffit-il ?

+
Non. Il voit ce que PHP voit. Un bind sale, un port 3306, un secret CI : hors sujet. On le pose après, comme filet, une fois l’image et les volumes repris.
À lire ensuite
WordPress piraté Guide WordPress WordPress derrière reverse proxy Porte dérobée PHP Mots de passe d’application Déclarer mon site