Quand un site WordPress “tombe malade”, on a souvent deux réflexes. Le premier consiste à croire que tout se joue dans le code. Le second, plus pragmatique, consiste à regarder ce qui s’est réellement passé côté serveur au moment où le problème a démarré. Les logs Apache et Nginx ne racontent pas seulement l’histoire, ils donnent des indices concrets, parfois très tôt, sur le type d’infection, la méthode d’accès et l’étendue du dégât.
Cette approche marche particulièrement bien pour WordPress, car une compromission suit souvent un schéma récurrent: des tentatives d’accès à des endpoints connus, des uploads de fichiers via des chemins atypiques, des téléchargements de scripts distants, puis une exécution qui modifie le comportement en front (redirections, iframes, chargements de scripts externes). Même si la “preuve” absolue se fait sur le système et les fichiers, les logs vous indiquent où chercher et dans quel ordre.
Ce que les logs peuvent vraiment révéler (et ce qu’ils ne disent pas)
On lit souvent les logs comme un rapport judiciaire: on cherche la “signature” du coupable. En pratique, c’est plus nuancé.
Les logs web (accès, erreurs) montrent:
- des requêtes entrantes, donc l’intention (exploration, scan, tentative d’upload, téléchargement de payload) les chemins ciblés, donc les éléments visés dans WordPress (wp-admin, wp-content, php, uploads, fichiers cachés) l’apparition d’outils automatisés via des patterns d’User-Agent, des taux d’erreurs ou des séquences de requêtes cohérentes
En revanche, les logs ne vous diront pas toujours:
- ce qui s’est passé à l’intérieur de PHP, si l’application n’émet pas d’erreurs exploitables si un fichier a été modifié sans passer par une route web (par exemple, via un accès SSH compromis, via une faiblesse ailleurs, ou via une tâche qui tourne sur le serveur) la totalité de l’impact si le serveur ne conserve pas assez longtemps ses journaux ou si la rotation les a fait disparaître
Mon conseil, quand vous explorez: ne cherchez pas uniquement “une attaque”. Cherchez un “chemin”. Une intrusion laisse souvent une trajectoire dans le temps, avec des requêtes répétées et des effets visibles ensuite (réponses, redirections, nouveaux fichiers, pics d’erreurs).
Les fichiers de logs à prioriser: accès, erreurs, et rotation
Sur un serveur Linux classique, vous verrez au minimum deux types:
- les logs d’accès (souvent dans formats avec statut, taille, méthode, URI, référent, user-agent) les logs d’erreurs (PHP, Nginx/Apache, refus, erreurs de parsing, messages de module)
Sur Apache, les chemins typiques peuvent ressembler à /var/log/apache2/access.log et /var/log/apache2/error.log (ou variante selon votre distribution). Sur Nginx, c’est souvent /var/log/nginx/access.log et /var/log/nginx/error.log.
Le point qui change tout en incident response: la rotation. Si vous n’avez que le dernier fichier compressé, vous risquez de rater les premières heures. Je me suis déjà retrouvé sur un cas où l’infection avait commencé 48 heures plus tôt, et le pattern d’upload n’apparaissait plus parce que l’accès avait déjà été purgé. À ce stade, la collecte devient une partie du “nettoyage”: documenter ce qui manque et ce que vous pouvez encore inférer.
Si vous avez la main, regardez:
- combien de fichiers rotatifs existent leur période couverte la présence de logs compressés (souvent .gz), utiles pour remonter
L’angle mort classique: l’infection ne commence pas toujours dans wp-content
Oui, la plupart des compromissions WordPress finissent par toucher wp-content (plugins, thèmes, uploads). Mais avant cela, il y a souvent une phase de reconnaissance ou d’accès à une fonction qui permet l’écriture.
Dans les logs, cela se traduit parfois par des requêtes qui “ne ressemblent pas à un upload” au sens strict. On voit par exemple:
- des tentatives sur /wp-json/ pour tester des endpoints des accès à des pages d’administration avec des patterns de bruteforce des requêtes vers des fichiers de configuration, des endpoints de récupération, ou des ressources qui n’existent pas des téléchargements de type “fetch” depuis un serveur externe, ensuite suivis de modifications locales (les logs d’accès montrent alors des requêtes successives, puis un changement de comportement)
Ce que j’essaie d’obtenir, c’est une chronologie: d’abord l’accès, ensuite la tentative d’écriture, puis enfin les symptômes front. Si vous commencez le nettoyage par les fichiers modifiés alors que la cause est ailleurs, vous risquez de recoder un “tunnel” déjà maintenu par un autre vecteur (un compte admin ajouté, une clé d’API, un plugin abandonné, un webshell qui relance son téléchargement).
Lire les logs d’accès: repérer les signatures sans paniquer
Les logs d’accès ressemblent à une succession de lignes. Au lieu de les lire ligne par ligne, j’adopte une méthode simple, mais disciplinée: filtrer, puis corréler.
La première étape consiste à repérer les périodes où la dynamique change. Un site compromis connaît souvent:
- une hausse du nombre de requêtes par minute une hausse des codes 4xx ou 5xx des paths très spécifiques qui reviennent (mêmes URIs, mêmes paramètres) des référents bizarres, ou des requêtes sans référent du tout, en rafales
Deuxième étape: identifier les IP “actives” et leur rôle. Une IP malveillante peut apparaître dans des heures où le trafic normal est faible, puis ne plus rien dire ensuite. Ou au contraire, elle peut revenir en fin de journée pour maintenir la persistance.
Troisième étape: regarder les patterns d’URL. Pour WordPress, certains chemins attirent immédiatement l’œil parce qu’ils sont soit des cibles fréquentes, soit des indicateurs de scripts qui tentent d’échapper à la détection.
Voici des signaux typiques que j’ai rencontrés, sans dire qu’ils sont universels:
- des requêtes vers des fichiers PHP dans des emplacements non attendus (par exemple des .php dans des sous-dossiers atypiques) des requêtes vers des noms de fichiers aléatoires ou pseudo-aléatoires, créés puis supprimés des tentatives d’accès à des endpoints connus, puis une séquence de requêtes répétées sur des URIs de même forme des chargements de ressources externes (dans les logs, on voit surtout la requête initiale, mais si le site redirige, vous le verrez comme une suite de réponses inhabituelles) des URIs contenant des paramètres suspects, souvent très encodés, parfois avec base64 ou des expressions qui ressemblent à des injections
Vous pouvez maintenant tomber sur le piège de la chasse au “tout est infection”. Une galerie d’URL inconnues peut aussi correspondre à un bot de scan non destructif, ou à un plugin qui expose des endpoints en erreur. Le bon réflexe est de croiser avec les logs d’erreurs et avec l’état réel des fichiers.
Les logs d’erreurs: la partie “ciblage” du problème
Les logs d’erreurs sont souvent plus parlants que les logs d’accès, parce qu’ils capturent les événements côté runtime: erreurs PHP, fichiers introuvables, permissions, messages d’exécution.
Sur Nginx, vous verrez souvent des lignes indiquant qu’un fichier a été refusé, qu’un script a renvoyé une erreur, ou que le serveur a tenté de traiter un fichier comme dynamique.
Sur Apache, selon la configuration et les modules, vous pouvez voir:
- des erreurs liées à la résolution de fichiers des messages PHP (via le module approprié ou les settings) des logs du reverse proxy si le site est derrière un composant
Quand je fais un tri en incident WordPress, j’imprime mentalement un schéma. D’abord, il y a des requêtes web. Ensuite, s’il y a exécution de PHP malveillant, les erreurs peuvent apparaître (et si l’attaquant fait bien les choses, il essaiera souvent de masquer ces erreurs). Donc vous pouvez avoir peu d’erreurs mais beaucoup d’accès suspects. À l’inverse, vous pouvez avoir des erreurs de permission ou d’accès à des fichiers, sans que les requêtes soient spectaculaires.
La valeur des logs d’erreurs est de vous aider à dire: “les requêtes ont réellement déclenché quelque chose”. Même une erreur 403 ou 404 peut révéler un pattern d’intention, par exemple un script qui s’attend à trouver un fichier et ne le fait plus, ou qui tente d’écrire mais échoue.
Une mini grille de corrélation (ce que je regarde dans l’ordre)
Je garde toujours le même ordre, parce que l’ordre réduit les fausses pistes:

1) Les pics et leur timing, pour trouver “quand” ça a commencé
2) Les IP dominantes sur les périodes anormales 3) Les URIs répétées et leurs formes (extensions, emplacements, paramètres) 4) Les codes de réponse, surtout 200 sur des chemins bizarres ou 302/301 de redirection 5) Les logs d’erreurs autour de la même fenêtre de temps 6) Ensuite seulement, l’inventaire des fichiers et la validation WordPressSi vous faites ça, vous allez éviter de nettoyer “au hasard”. Vous allez nettoyer ce qui est réellement corrélé.
Pour rendre ça concret, voici un petit set de signaux que vous pouvez chercher dans vos journaux (accès et erreurs). Ce ne sont pas des preuves en soi, mais ce sont de très bons points de départ:
- URIs vers des scripts PHP dans des emplacements inattendus (en particulier sous wp-content, uploads, ou des chemins qui ne contiennent normalement pas d’exécution directe) séquences répétées avec des paramètres encodés ou anormalement longs, qui reviennent sur plusieurs lignes redirections répétées ou 200 sur des pages que personne ne visite normalement, souvent avec une chaîne de requêtes en cascade erreurs de permission, de fichier introuvable, ou d’exécution PHP au même moment que des requêtes suspectes tentatives d’accès à des endpoints d’administration ou des endpoints JSON suivies, peu après, de requêtes vers des ressources d’écriture ou de chargement
Deux cas fréquents: compromission “silencieuse” et défiguration rapide
Tous les sites compromis ne se comportent pas pareil, et ça change la façon de lire les logs.
Compromission silencieuse
Parfois, l’attaquant dépose un script qui n’exécute pas forcément tout de suite. Il attend une condition: un user-agent particulier, une géolocalisation, une URL précise, ou un moment. Dans les logs d’accès, vous verrez alors des “accès” plus irréguliers, sans pic massif immédiatement.
Le piège dans ce cas: nettoyer uniquement les fichiers visibles. Si le webshell contient une logique de persistance (téléchargement à la demande), il peut être réactivé après coup. Les logs aident ici car ils montrent si, après votre premier nettoyage, un nouveau cycle de requêtes se met à repartir. S’il repart, il y a une persistance ailleurs.
Défiguration rapide
À l’autre extrême, certains incidents sont visibles en quelques minutes. Les logs montrent des chemins moins “furtifs”, par exemple des téléchargements rapides puis une réécriture de contenu. Vous pouvez voir de la redirection, de nouveaux fichiers, et parfois des erreurs de compilation PHP.
Dans ce scénario, le nettoyage doit être immédiat, mais pas uniquement cosmétique. Si vous remplacez des fichiers sans vérifier l’ensemble, l’attaquant peut revenir avec un autre payload. Mon approche ici est: isoler le site, collecter les preuves, puis nettoyer en profondeur.
Nettoyer un site WordPress infecté, sans casser ce qui doit l’être
Le nettoyage d’un WordPress compromis mérite un cadre. Vous ne voulez pas juste supprimer un fichier suspect et espérer que tout s’arrête. Souvent, la compromission consiste en:
- un webshell ou un fichier malveillant ajouté un plugin ou thème modifié une base de données modifiée (options, articles injectés, comptes admin, liens cachés) une persistance via un compte utilisateur, un cron WordPress, un upload renommé, ou un vecteur externe
Les logs Apache/Nginx vous orientent sur les fichiers et sur le vecteur initial. Ensuite, vous passez à l’action de manière structurée.
Ce que vous pouvez faire tout de suite
Quand vous suspectez une infection, vous gagnez du temps si vous gérez la triade: preuves, interruption, restauration.
Vous voulez limiter la propagation pendant que vous sauvegardez. Par exemple, si vous avez une page d’administration accessible, un attaquant peut continuer à exécuter du code tant que le site est en ligne.
Voici une liste courte de mesures immédiates, pratico-pratiques, que j’applique souvent quand je commence à analyser des logs:
- mettez le site en mode maintenance (ou bloquez temporairement l’accès externe si possible) sauvegardez logs, fichiers, et base de données avant modifications identifiez les périodes et IPs depuis les logs pour savoir “qui” et “quand” isolez les fichiers suspects identifiés par les chemins ciblés dans les logs lancez ensuite un nettoyage contrôlé, pas un “nettoyage à la main” au feeling
Cette liste n’est pas une recette magique, c’est un cadre de réduction de risque. La priorité est de ne pas détruire une preuve et d’empêcher la persistance de continuer https://gardewp.fr/ à fonctionner.
Nettoyer avec méthode: fichiers d’abord, puis base de données
Une partie du débat “fichiers ou base d’abord” dépend du type d’incident. Si vos logs montrent des requêtes vers des scripts PHP uploadés, les fichiers d’abord sont souvent plus logiques. Si vous voyez des comportements de redirection, de modification de contenu, et des traces côté https://gardewp.fr/nettoyage-malware-wordpress/ base (compromission d’options, comptes admin), alors la base doit être inspectée rapidement.
Dans la réalité, vous faites souvent les deux, mais dans un ordre qui suit l’évidence:
1) Remettre WordPress et les plugins/thèmes à une version saine (en pratique, re-télécharger et remplacer, pas juste “supprimer ce fichier”). 2) Supprimer tout fichier ajouté hors des emplacements attendus. 3) Vérifier la base: utilisateurs, options, transients, cron, contenus injectés. 4) Rechercher des traces de persistance (webhooks, hooks, cron système, cron WordPress).
Je garde aussi un œil sur la cohérence des dates des fichiers. Si un fichier a été modifié exactement au moment où une IP a déclenché un pattern d’URL très suspect dans les logs, c’est un candidat fort.
Comment relier une ligne de log à un fichier concret
C’est là que beaucoup de procédures s’effondrent. On voit “une requête vers un chemin”. Ensuite, on cherche un fichier. Mais il manque parfois la traduction entre l’URI et le système de fichiers, surtout si la configuration Apache ou Nginx a des règles de rewrite.
Quelques cas utiles à connaître:
- si l’URL correspond directement à un fichier, vous pouvez chercher le chemin dans DocumentRoot (ou dossier du vhost) si un rewrite transforme l’URI, l’entrée des logs peut être trompeuse, mais vous pouvez retrouver le fichier cible via la configuration (si vous y avez accès) si l’attaquant utilise des paramètres pour exécuter du code, le chemin peut ressembler à quelque chose de légitime, mais le paramètre trahit l’intention
Ce que je fais, concrètement, c’est de partir des URIs suspectes et de recouper avec:
- les extensions (php, phtml, autre) les emplacements (uploads, caches, répertoires de plugins) les noms de fichiers (souvent aléatoires) les dates de modification
Si vos logs montrent une requête vers un fichier inexistant puis, quelques minutes plus tard, une autre requête de type “réussite”, vous avez peut-être une étape où le fichier a été uploadé puis servi ensuite.
Confirmer l’élimination: vérifier dans les logs que la persistance a cessé
Un nettoyage, pour être “fini”, doit se refléter dans les logs. Sinon, vous nettoyez au niveau du symptôme.
Après restauration, je surveille:
- les IPs suspectes: réapparaissent-elles dans les mêmes fenêtres horaires? les mêmes URIs: reviennent-elles? la diversité d’URLs: si elle s’élargit, l’attaquant teste souvent de nouvelles voies les codes de réponse: les 200 sur des paths bizarres disparaissent-ils?
C’est aussi utile pour détecter un faux positif. Parfois, on supprime un fichier “suspect” mais non actif. Les logs permettent de confirmer que le motif d’attaque a réellement cessé.
Si vous voyez une répétition même après vos actions, il faut remonter un niveau. Cela peut vouloir dire:
- un compte utilisateur admin a été conservé un cron WordPress a été ajouté un plugin a été réinfecté via une chaîne de téléchargement récurrente un autre emplacement est compromis, par exemple la machine elle-même ou un autre service
À ce moment, les logs ne suffisent plus. Mais ils indiquent où creuser en premier.
Edge cases qui trompent l’enquête
Quelques situations rendent la lecture des logs plus complexe, et je préfère les mentionner plutôt que promettre une lecture “automatique”.
Premièrement, certains scanners génèrent beaucoup de bruit, avec des 404 et des chemins improbables. Ce bruit peut masquer le vrai incident. Le bon critère est l’existence de succès (200 ou 302/301) sur des chemins qui ne correspondent pas à votre trafic normal, combinée à des modifications de fichiers ou des symptômes.
Deuxièmement, un reverse proxy ou un CDN modifie la visibilité. Les logs que vous regardez peuvent être ceux du proxy, pas ceux du serveur applicatif. L’URI peut être identique, mais l’IP source peut être masquée. Dans ce contexte, vos corrélations temporelles restent valables, mais l’attribution IP devient plus délicate.
Troisièmement, certains systèmes ne journalisent pas assez. Si votre Nginx/Apache ne conserve pas les headers, vous perdez user-agent et référent. Vous pouvez quand même travailler avec l’URI et les statuts, mais vous serez moins efficace pour identifier des toolings spécifiques.
Quatrièmement, le “nettoyer site WordPress infecté” implique parfois aussi de corriger le vecteur. Si la faiblesse initiale n’est pas corrigée, le site peut être réinfecté en quelques minutes. Les logs montrent alors un retour très rapide des mêmes patterns.
Un exemple de trajectoire typique, vue dans les logs
Je vous donne un exemple réaliste de la forme d’un incident, sans prétendre que c’est votre cas exact.
Imaginez une fenêtre nocturne. Vous observez d’abord des requêtes répétées sur des chemins d’administration ou des endpoints qui n’existent pas dans votre configuration. Ensuite, une séquence plus “utile” apparaît, avec des URIs pointant vers des fichiers PHP logiquement situés ailleurs que votre WordPress standard, souvent sous wp-content. Enfin, peu de temps après, vous voyez des réponses 200 sur ces chemins, et votre front commence à renvoyer une autre ressource, ou à charger un script externe.
En parallèle, les logs d’erreurs montrent quelques messages autour du même moment: refus d’accès, erreurs PHP, ou warnings. À partir de là, vous identifiez les fichiers dont les dates de modification collent avec cette fenêtre. Vous remplacez WordPress et les plugins propres, vous supprimez ce qui ne correspond pas à votre référentiel sain, puis vous inspectez la base. Quand vous repassez le site en ligne, vous surveillez les logs, et vous constatez que les mêmes URIs ne reviennent plus. Si elles reviennent, vous savez que la persistance n’a pas été éradiquée.
Ce n’est pas de la magie. C’est de la cohérence temporelle.
Ce que vous devriez préparer pour la prochaine fois
Il y a une chose qui rend les logs utiles dès le premier jour. C’est d’être prêt avant la crise.
Sans faire de sur-architecture, vous pouvez gagner énormément en:
- conserver les logs sur une durée suffisante (selon votre capacité et vos contraintes) centraliser les logs si vous avez plusieurs serveurs augmenter le niveau de détail des logs d’erreurs quand vous déployez des changements sensibles documenter les versions et plugins installés, pour savoir ce qui est normal
Je sais que tout le monde ne peut pas changer l’infra à chaque alerte. Mais au moins, gardez une trace de votre configuration, car une partie de la lecture des logs dépend de la façon dont Apache ou Nginx traduit l’URI.
Plan de travail recommandé, orienté logs
Voici une manière de combiner analyse et nettoyage, sans tomber dans des étapes trop “théoriques”. Je le fais mentalement comme un fil conducteur: logs vers évidence, évidence vers action, action vers validation.
Commencez par dater: repérez la fenêtre d’activité anormale. Ensuite, isolez les IPs et les URIs. À partir des URIs, cherchez dans l’arborescence et vérifiez les modifications datées. Vous nettoyez ensuite WordPress et les composants à remplacer par une version saine. Puis vous inspectez la base pour les modifications qui ne sont pas toujours visibles dans les logs d’accès.
Enfin, vous revenez aux logs pour valider: le motif doit disparaître. Si le motif continue, vous repartez de la fenêtre initiale et vous cherchez la persistance.
Dans ce processus, les mots “nettoyer site WordPress infecté” prennent un sens concret. Ce n’est pas juste supprimer un fichier. C’est interrompre un comportement, puis prouver qu’il ne revient plus.
Dernier point: quand les logs ne suffisent plus
Il existe des cas où les logs web ne peuvent pas vous donner la réponse complète: logs incomplets, attaque côté système, compromission via accès SSH, automatisation qui n’émet pas de requêtes web évidentes, ou configuration qui masque les détails.
Dans ces scénarios, les logs restent utiles pour:
- détecter que “quelque chose” se passe déterminer les heures à auditer côté OS fournir une liste d’IP et de patterns à recouper
Mais le nettoyage devra alors s’appuyer sur:
- vérification des comptes système audit des tâches planifiées contrôle des logs système et des mécanismes de persistance comparaison stricte de l’état des fichiers avec une référence saine
Même alors, la discipline acquise via Apache/Nginx sert de boussole. Vous savez où chercher, et surtout dans quel ordre.
Si vous voulez, décrivez votre configuration (Apache ou Nginx, type d’hébergement, durée de conservation des logs, exemple de 10 à 30 lignes d’accès et d’erreurs autour de la fenêtre suspecte en masquant les IP et tokens). Je peux vous aider à interpréter les patterns et à établir une stratégie de nettoyage réaliste, adaptée à vos logs.