WordPress derrière un reverse proxy : logs et IP réelles
Les journaux WordPress et Apache du container montrent l’IP du proxy. Sans les en-têtes d’adresse réelle, vous cherchez un attaquant qui s’appelle 172.18.0.1. Un WAF mal réglé cache aussi vos propres tests. On configure la lecture, on ne « s’infiltre » pas.
Les journaux montrent l'IP du proxy. Configurez les en-têtes réels pour comprendre l'entrée. Un WAF mal réglé peut aussi cacher l'attaquant et vos propres tests.
Ce que « derrière un proxy » change au diagnostic
Traefik, nginx proxy, Cloudflare, le Varnish de l’hébergeur, un load balancer OVH : WordPress n’entend plus le client. Il entend le hop suivant. Les plugins « limite de connexions », les journaux d’échec de login, les « IP qui postent du spam » deviennent le proxy. Vous bloquez 172.18.0.1 et vous coupez tout le site, ou vous ne voyez rien et vous croyez à un fantôme.
Le nettoyage du disque reste le même (guide WordPress). La chronologie, elle, se lit plus haut dans la chaîne. Un kit servi uniquement depuis le cache du proxy alors que le fichier est déjà retiré : l’inspection Google voit encore le HTML. Vous jurez que le FTP est propre. Les deux est vrai.
Nous commençons par un schéma fléché : visiteur → CDN / WAF → proxy local → PHP. Chaque flèche a des logs. Tant que le schéma n’est pas posé, on ne discute pas des mu-plugins.
- Nommer chaque hop (Cloudflare ? nginx du VPS ? Apache du container ?).
- Savoir qui termine le TLS.
- Savoir qui a un cache de page.
Qui est 172.x, 10.x, cf-connecting-ip
172.16–31, 10.x, 192.168.x dans access.log : réseau Docker ou LAN. Ce n’est pas « l’attaquant à Lyon ». CF-Connecting-IP, True-Client-IP, X-Real-IP, X-Forwarded-For : candidats à l’IP publique, seulement si vous faites confiance au hop qui les pose.
WordPress peut montrer une IP réelle dans « Santé du site » ou dans un plugin une fois le réglage fait (reverse proxy / HTTP_X_FORWARDED_FOR). Mal réglé, n’importe quel client envoie un X-Forwarded-For fantaisiste et vous accusez un innocent — ou vous ratez l’entrée.
Pour un constat d’incident, l’IP utile est celle du journal du hop le plus proche d’Internet que vous contrôlez (Cloudflare analytics, logs nginx edge). L’origine sert à dater les requêtes internes (quelle URL, quel user-agent), pas à géolocaliser.
Configurer la confiance (sans ouvrir le monde)
On ne dit à WordPress / PHP de « croire X-Forwarded-For » que pour les IP de vos proxys. Une confiance globale transforme l’en-tête en jouet. Chez nginx : set_real_ip_from les réseaux du proxy, puis real_ip_header. Chez Apache : mod_remoteip avec la même liste. Chez Cloudflare : leurs plages, pas 0.0.0.0/0.
wp-config : certains tutoriaux ajoutent des $_SERVER['REMOTE_ADDR'] = … à la hache. Après incident, relisez ces lignes : un attaquant les aime autant que vous. Comparez à une copie connue.
Objectif : des logs d’origine qui, demain, montrent une IP publique pour un login wp-admin. Pas de magie, pas de scan du net. Si vous ne savez pas quelles plages faire confiance, demandez-les à l’hébergeur / au prestataire CDN — c’est leur liste, pas un secret d’attaque.
WAF : faux négatifs, faux positifs, tests aveugles
Un WAF en « under attack » peut vous montrer un challenge et laisser passer Googlebot (ou l’inverse). Vos tests « le site est propre » depuis le bureau, cookies d’admin, IP allowlistée, ne voient pas ce que voit un client Orange. Désactivez temporairement l’allowlist pour un test, ou utilisez le téléphone 4G et l’inspection Search Console.
Le WAF n’a pas retiré le PHP du volume. Il a peut-être masqué un symptôme (injection XSS visible) et laissé le backdoor. S’en servir comme unique nettoyage est le même échec qu’un plugin « cleaner ». On le remet en filet après.
Des règles trop larges cassent wp-cron, les webhooks WooCommerce, le renouvellement ACME. Pendant l’incident, notez ce que vous changez (mode, règles) pour ne pas diagnostiquer un 403 comme un défacement.
Caches : le malware qui n’est plus sur le disque
Page cache (plugin, nginx fastcgi, Cloudflare, Varnish) : purge ciblée sur les URL de kit et globale ensuite. Un 410 à l’origine + 200 au edge = réexamen refusé.
Cache objet (Redis, Memcached) : un objet PHP sérialisé sale survit au fichier propre. Redémarrer / vider après nettoyage. Voir le même réflexe que pour un object-cache.php drop-in.
CDN d’images : un PHP hébergé comme « image » est un autre article. Ici : vérifiez que le proxy ne sert pas un ancien HTML pour / (home défigurée encore en cache 2 h).
HTTP vers l’origine, HTTPS devant
Cloudflare Flexible : le visiteur est en HTTPS, l’origine en HTTP. L’origine peut être la face sale décrite dans HTTP encore ouvert. Passez en Full (strict) quand le cert d’origine est valide, ou nettoyez aussi le vhost :80.
WordPress doit connaître l’URL publique (https://www…) sinon il génère des liens http, des mixed content, et des redirections absurdes. Constantes WP_HOME / WP_SITEURL alignées sur l’origine publique, pas sur http://wordpress.internal.
Un mauvais Host transmis (le nom interne) crée des canonicals et des sitemaps internes. Après incident, régénérez sitemap et vérifiez Search Console — propriété domaine.
Journaux à demander (proxy + origine)
Au CDN : requêtes sur les chemins de kit, codes, user-agents, pays. À l’origine : même chose + temps PHP. Les deux horodatages, même fuseau. C’est ce qui distingue un crawl Safe Browsing d’un POST de formulaire.
Journaux d’erreur du proxy (502 pendant que vous vidiez PHP-FPM) : ne les prenez pas pour une attaque. Journaux d’auth du WAF : utiles pour voir si quelqu’un martèle wp-login depuis l’extérieur — l’IP sera enfin publique si le WAF logue correctement.
Gardez une extraction. Réinstaller l’origine « pour se rassurer » les détruit. Les premiers gestes : copier avant.
Après l’incident : une chaîne lisible
Schéma à jour, plages de confiance documentées, purge connue (qui, comment). Moins d’allowlist permanente « le bureau ». Tests mobiles dans la recette de remise en ligne.
Si personne dans l’équipe ne sait dire qui pose CF-Connecting-IP, déclarez. On lit la chaîne. Pas d’exploit, pas d’accès à l’inscription.
Le site trompeur se lève quand l’edge sert du propre, pas quand le container est propre tout seul. Purge + inspection, puis retrait blacklist.