Audit WordPress : protéger l’environnement de déploiement (CI/CD)

Une application WordPress peut sembler simple à déployer: on met des fichiers, on configure un thème, on pousse un zip, et c’est réglé. Le problème, c’est que WordPress n’est pas seulement du code applicatif. C’est aussi une chaîne de dépendances, d’accès, d’identifiants, de plugins, d’archives, de caches, et surtout un enchaînement de responsabilités entre votre système CI/CD et l’environnement où le site tourne.

image

Quand on parle d’« audit et sécurisation WordPress », on pense souvent à WordPress lui-même: mises à jour, durcissement, droits sur les fichiers, plugins réputés, permissions MySQL, sécurité des thèmes. Pourtant, l’environnement de déploiement est un point de bascule. Un attaquant qui compromet la pipeline CI peut publier une version “correcte” côté code, mais malveillante côté contenu, injecter une porte dérobée, exfiltrer des secrets ou modifier le script de déploiement. Et le pire, c’est qu’on peut avoir des logs propres, des checks qui passent, et des artefacts qui ont l’air légitimes.

Ce billet décrit comment j’aborde un audit orienté CI/CD pour WordPress, avec des décisions pragmatiques, des scénarios réalistes, et des garde-fous qui protègent sans transformer votre équipe en chantier permanent.

Là où la sécurité se joue vraiment

Dans un déploiement WordPress, la pipeline construit un artefact (zip, bundle, image), l’authentifie, l’envoie, puis déclenche un “release” dans un environnement. Les surfaces d’attaque se concentrent sur quelques zones:

    La gestion des secrets dans le CI (tokens, clés SSH, identifiants API, passwords de base). Le transport et l’intégrité de l’artefact (est-ce le bon zip, signé, non modifié, non re-packagé?). Les droits de l’utilisateur de déploiement (peut-il écrire partout? Peut-il lancer des commandes arbitraires?). Les scripts de déploiement (souvent négligés, parfois écrits à la va-vite). Les mécanismes de rollback et de promotion (ce qui est déployé en production est-il identique à ce qui a été testé?).

J’ai vu des situations où WordPress était bien durci sur la machine cible, mais où la CI utilisait une clé SSH trop permissive, et où un simple echo accidentel dans un script révélait un token. L’application était “safe”, jusqu’au jour où l’accès au déploiement a été détourné.

Préparer l’audit: cartographier la chaîne CI/CD

Avant de toucher aux configurations, je commence par une cartographie simple, orientée “qui peut faire quoi”. On cherche à répondre, sans jargon et sans supposer que tout le monde a le même modèle mental:

    Où se trouvent les secrets (variables CI, gestionnaire de secrets, fichiers dans le repo)? Quel utilisateur exécute le déploiement côté cible (SSH, agent, runner, compte système)? Quels chemins sont modifiés lors du déploiement (wp-content, mu-plugins, uploads, config, scripts)? Comment la pipeline est déclenchée (pull request, push main, tags de release)? Comment on passe de staging à production (même artefact, ou rebuild à chaque environnement)?

Ce travail est souvent sous-estimé parce qu’il semble “administratif”. En réalité, il conditionne tout le reste. Sans cartographie, on risque de sécuriser un maillon, tout en laissant une autre porte ouverte, par exemple un rebuild en production qui remplace l’artefact testé en staging.

Vérifier l’intégrité de ce qui est déployé

Le contrôle le plus rentable, à mon avis, consiste à garantir que ce qui a passé les tests est exactement ce qui arrive dans l’environnement. L’approche “ça a l’air correct parce que le pipeline a réussi” est fragile. En pratique, la CI peut reconstruire l’artefact à différents moments, ou re-compiler, ou re-packager au moment du déploiement.

Pour réduire ce risque, je vise une règle simple: un artefact produit une fois, identifié de manière reproductible, promu ensuite. Le niveau de détail dépend de votre maturité.

image

    Si vous produisez un zip WordPress et que vous ne changez pas son contenu après les tests, vous pouvez calculer une empreinte (hash) et la comparer au moment de déployer. Si vous construisez une image Docker, idéalement vous jouez avec des digests et des signatures, ou au minimum vous verrouillez la provenance. Si vous refaites une étape de build en production, vous créez une divergence potentielle. Même si c’est “le même repo”, les plugins téléchargés dynamiquement, les versions non verrouillées, ou une étape externe peuvent diverger.

Je traite aussi un cas fréquent: les pipelines qui “injectent” des fichiers au moment du déploiement. Par exemple, un script télécharge un plugin depuis un URL externe ou ajoute un fichier de configuration local. C’est parfois nécessaire, mais ça doit être explicite, audité et contrôlé.

Diminuer l’impact si la CI est compromise

Supposons que le runner ou la pipeline soit compromise. L’objectif devient alors de limiter ce que l’attaquant peut faire. Je raisonne en termes de confinement.

Le premier levier est de minimiser les secrets exposés au runtime de la pipeline. Une règle que j’applique souvent: donner seulement les permissions nécessaires, et uniquement au job qui en a besoin. Un token de déploiement ne devrait pas être accessible à tous les jobs, surtout ceux qui exécutent des linters ou des tests qui pourraient charger des dépendances.

Le deuxième levier est le principe du moindre privilège côté cible. Le compte utilisé pour déposer les fichiers ne devrait pas être root, et idéalement il ne devrait pas avoir la possibilité d’exécuter n’importe quelle commande. Si votre script de déploiement utilise SSH, vous pouvez aussi envisager de restreindre le compte SSH avec des configurations qui autorisent uniquement l’upload vers des répertoires déterminés et des commandes précises.

Le troisième levier concerne les logs. Beaucoup d’attaques cherchent à extraire des secrets par erreurs de debug, ou par affichage involontaire. Je vérifie que:

    les variables sensibles sont masquées, les commandes ne loguent pas des tokens en clair, les erreurs d’authentification ne renvoient pas des informations exploitables.

Un détail pratique: certaines plateformes de CI masquent mieux les secrets si elles sont déclarées dans le bon format de variable, pas si elles sont injectées via des scripts intermédiaires.

Sécuriser les secrets: stockage, rotation, usage

La sécurité des secrets ne se limite pas à “utiliser un variable dans CI”. Un audit sérieux regarde la chaîne de vie des identifiants. Est-ce que la clé change facilement? Est-ce que vous avez un mécanisme de rotation? Qui peut voir les valeurs? Que se passe-t-il en cas de fuite?

Sans inventer de technologie, je peux vous donner des critères concrets, faciles à auditer:

    Les secrets sont-ils chiffrés et stockés dans un système dédié (gestionnaire de secrets) plutôt que dans le repo? Les droits CI sont-ils séparés par environnement (staging et production)? Les secrets sont-ils limités au job qui en a besoin? Les secrets sont-ils réutilisés à travers plusieurs pipelines? Avez-vous une procédure de rotation et des canaux d’urgence?

J’ai déjà vu des pipelines où une seule clé SSH était utilisée partout, y compris pour des environnements qui n’avaient aucune raison d’y accéder. Quand la clé fuit, l’attaquant n’a plus besoin de “deviner” quoi que ce soit.

Traiter les plugins et dépendances sans introduire de surprise

En WordPress, une partie du risque vient des plugins. Si votre CI télécharge des plugins au moment du build, vous introduisez un dépendance externe. C’est parfois inévitable, mais il faut verrouiller.

Lors de l’audit, je recommande de clarifier la stratégie:

    Soit les plugins sont vendus en tant que contenu “déjà contrôlé” dans l’artefact (ce qui réduit la variabilité). Soit vous les récupérez depuis des sources externes au moment du build. Dans ce cas, vous devez définir des versions exactes, et éviter les “latest”, les branches mouvantes, et les scripts qui font des téléchargements sans validation.

Un piège courant: les pipelines qui reposent sur des commandes système qui “réparent” une installation en direct. On croit gagner du temps, mais on perd le contrôle. Dans les audits, je demande souvent: “qu’est-ce qui doit être stable, et qu’est-ce qui peut être recalculé?” Une dépendance recalculable, c’est acceptable. Une dépendance non verrouillée, c’est une surface d’attaque.

Contrôler les déclencheurs et limiter le scope des modifications

La pipeline peut se déclencher sur des événements qui exposent votre système. Exemple: les pipelines “pull request” qui déploient directement sur staging, ou des workflows qui donnent des secrets à des jobs exécutés sur des commits provenant de forks.

Ce point est surtout critique quand votre plateforme de CI accepte les PR depuis des contributeurs externes. Même si vous limitez la branche cible, il peut exister des chemins où le code d’une pull request peut exécuter des scripts dans votre contexte CI.

Je vérifie deux choses:

    Est-ce que les jobs de type “build/test” ont des accès séparés de ceux qui déploient? Les secrets de déploiement sont-ils disponibles seulement sur les branches de confiance, sur les tags, ou après validation explicite?

On peut concilier sécurité et vitesse via un modèle “preview” sans secrets, puis déploiement uniquement après merge. Ce n’est pas toujours “automatique”, mais c’est plus sûr.

Réduire les erreurs de configuration côté déploiement

Beaucoup de problèmes CI/CD ne sont pas des failles au sens strict, mais des erreurs de configuration qui créent des conditions dangereuses. Par exemple:

    Déploiement qui écrase des fichiers critiques avec mauvais droits. Scripts qui changent les permissions de manière trop large, par réflexe. Usage d’archive mal structurée, qui mélange uploads et code applicatif. Variables d’environnement mal nommées qui finissent dans des fichiers log.

Je conseille de séparer clairement ce qui relève du code (thèmes, mu-plugins, core si vous le versionnez) et ce qui relève des données (uploads, base de données). Si vous mélangez les deux dans l’artefact, un rollback peut effacer des contenus. Et si vous donnez les mêmes droits sur les deux, vous augmentez la portée d’un incident.

Dans un audit, je demande souvent un exemple concret de déploiement: “montrez-moi le dossier final, expliquez-moi le chemin de fichiers sur la machine cible, et dites ce qui est censé être modifié”. Cette simple question révèle des habitudes, parfois très anciennes, difficiles à corriger autrement.

Exemple de checklist d’audit pour la pipeline

Voici la manière dont je condense l’audit au moment de passer en mode “action”. Ce https://gardewp.fr/securite-wordpress/ n’est pas une liste théorique, c’est une base de conversation avec l’équipe qui maintient la CI.

Vérifier que les secrets de déploiement ne sont disponibles que dans les jobs qui déploient vraiment, et uniquement sur les branches autorisées Imposer une promotion d’artefact identique entre staging et production, ou au minimum une validation par hash Restreindre les comptes et permissions utilisés sur la cible, éviter root, limiter les répertoires accessibles Contrôler ce que la CI télécharge pendant le build, verrouiller les versions et éviter les téléchargements “latest” Auditer les scripts de déploiement, surtout les étapes qui exécutent des commandes shell et celles qui manipulent des archives

Si vous devez choisir, commencez par 1 et 2. Ce sont souvent les plus impactantes par rapport à l’effort.

Détecter et répondre: logs, alertes, et garde-fous

Sécuriser ne veut pas dire seulement “empêcher”. Il faut aussi détecter et pouvoir réagir rapidement. Dans un environnement CI/CD, l’audit doit se prolonger par des exigences opérationnelles.

Je regarde la couverture des logs côté CI: sont-ils conservés assez longtemps, sont-ils corrélés à un identifiant de release, et est-ce que le déploiement mentionne clairement la version d’artefact? Ensuite, côté cible: l’historique de déploiement est-il traçable? Un attaquant peut effacer des traces au sein de votre script, si vos scripts ne journalisent rien.

Un signal utile consiste à enregistrer des métadonnées, comme:

    l’empreinte de l’artefact déployé, l’utilisateur ou le rôle qui a déclenché la promotion, l’heure et la cible (staging, production), éventuellement une liste courte des changements (taille d’uploads non pertinente, mais fichiers modifiés oui).

Sans transformer tout en système d’observabilité, vous gagnez un temps précieux le jour où il faut prouver quoi a été fait et quand.

Cas limites: ce qui complique la “pureté” du modèle

Certains problèmes CI/CD pour WordPress reviennent régulièrement, et chacun a un compromis.

Les uploads et médias

Si vos médias vivent dans wp-content/uploads, vous ne voulez pas les régénérer à chaque déploiement. En général, on les exclut de l’artefact de code. Mais alors, comment gère-t-on les migrations de structure ou des prérequis de thème? Il faut un mécanisme clair, et surtout une séparation des responsabilités.

Les bases de données

La base de données ne doit pas être reconstruite depuis la CI de façon brutale. On peut avoir des migrations, mais il faut un chemin de validation. Quand la CI applique une migration, elle devient un outil de modification de données sensibles. La sécurisation doit inclure la façon dont vous connectez la base, et la visibilité des erreurs.

Les caches et le front

Un déploiement WordPress déclenche parfois des purge de cache, ou un invalidation CDN. Ce n’est pas “juste un détail”. Si le purge CDN utilise un token exposé, vous ajoutez un secret de plus. Et si la purge s’exécute trop tôt, vous pouvez rendre “public” un état intermédiaire.

Dans mes audits, je demande une chronologie réelle du déploiement, minute par minute si nécessaire. L’ordre des opérations est une forme de sécurité.

Séparer code, exécution et configuration

Une recommandation fréquente consiste à “mettre tout le monde dans le repo”, mais CI/CD n’aime pas le flou. Un audit doit clarifier:

image

    Ce qui est versionné dans le code (thèmes, mu-plugins, éventuellement configuration de base non sensible). Ce qui est injecté au déploiement (config de base, variables d’environnement). Ce qui vit hors du repo (secrets, certificats, clés de connexion).

Cette séparation permet de limiter la fuite accidentelle. Elle aide aussi à comprendre pourquoi une erreur de build ne doit pas forcément exposer un secret: le code et la config sensibles doivent rester découplés.

Comparer deux modèles de déploiement: upload manuel vs déploiement pipeline

Pour aider à décider, je compare sans dogme. Le contexte change tout, mais voici une grille utile en audit.

| Critère | Déploiement via upload manuel | Déploiement via CI/CD automatisé | |---|---|---| | Risque de secret exposé | souvent élevé si les clés sont partagées entre personnes | dépend de l’isolation des jobs et du coffre à secrets | | Traçabilité | variable, parfois basée sur l’historique humain | généralement mieux corrélée au run de pipeline | | Cohérence artefact | parfois “presque le même”, mais avec dérive | meilleure si promotion d’artefact et contrôle d’intégrité | | Vitesse de réaction | correcte, mais dépend des disponibilités | bonne, avec rollback et déploiement reproductible | | Surface d’attaque CI | faible, mais accès aux serveurs plus diffus | à renforcer, car une CI compromise devient critique |

L’idée n’est pas de dire que l’automatisation est mauvaise. Elle est souvent indispensable. L’idée, c’est que l’automatisation déplace le risque vers la pipeline, donc l’audit doit être plus exigeant sur l’environnement de déploiement.

Mes recommandations finales, orientées “impact”

Je termine souvent mes audits par un plan d’actions réaliste. Sans surcharger la liste, voilà les priorités que je privilégie, parce qu’elles protègent la chaîne de déploiement au bon niveau.

D’abord, verrouillez ce qui est promu. Un artefact testé doit être le même artefact déployé. Ensuite, réduisez l’accès aux secrets, au bon périmètre, avec des droits minimaux. Enfin, faites en sorte que l’exécution sur la cible soit confinée et journaling-friendly. Un bon durcissement WordPress aide, mais sans sécurité de la pipeline, il reste une zone où tout peut être contourné.

Quand on ajoute à cela une discipline sur les plugins et dépendances, avec versions explicites et téléchargements contrôlés, on diminue le risque de “surprise” en production. Et quand vous ajoutez des logs corrélés et une empreinte d’artefact, vous gagnez en capacité de réponse, ce qui est souvent sous-évalué jusqu’au jour du incident.

Si vous deviez retenir une image: l’environnement CI/CD est le “port d’entrée” de votre WordPress. Sécuriser WordPress sans sécuriser ce port, c’est comme mettre une serrure solide à la porte principale tout en laissant la fenêtre arrière ouverte.