Protection WordPress : durcir la sécurité des permissions PHP

Quand on parle de sécurité WordPress, on pense souvent aux plugins, aux mots de passe, au durcissement côté réseau ou aux mises à jour. Pourtant, il existe un angle mort qui revient régulièrement sur le terrain: les permissions des fichiers et répertoires, en particulier quand PHP est impliqué. Un script PHP que l’on rend trop accessible, un dossier qui autorise l’écriture, ou un fichier sensible lisible par tout le monde, et l’incident ne vient pas du “piratage hollywoodien”, mais d’une erreur simple qui finit par coûter cher.

J’ai vu ce scénario se répéter sous des formes différentes. Un site “ça marche” pendant des mois, puis un plugin mal configuré ou un compte FTP compromis dépose un fichier au mauvais endroit. À partir du moment où les permissions laissent PHP exécuter ce qu’il ne devrait pas, une mauvaise action devient un vrai accès. La protection WordPress ne se joue pas uniquement dans le code, elle se joue aussi dans l’architecture des droits sur le disque.

Les permissions PHP: ce que WordPress ne vous dit pas

Sous WordPress, la logique “PHP exécute, HTML s’affiche” est évidente. Ce qui l’est moins, c’est que l’exécution PHP dépend de deux choses:

La façon dont le serveur associe certains chemins à PHP (handler Apache, PHP-FPM, configuration Nginx, mod_php, etc.); Les droits d’accès UNIX, c’est-à-dire qui peut lire, écrire, exécuter, et dans quels répertoires.

Le résultat peut être trompeur. Un fichier PHP “inofensif” placé dans un dossier non exposé à PHP ne sera pas exécuté, même s’il existe. À l’inverse, si ce fichier tombe dans un chemin où le serveur sert ou exécute du PHP, il devient une porte d’entrée. Les permissions ne créent pas à elles seules une vulnérabilité, mais elles déterminent si une erreur de configuration, un dépôt involontaire, ou un bug finit en incident.

Sur un site WordPress, on trouve souvent trois zones sensibles:

    les fichiers racine et de configuration (comme wp-config.php); les répertoires où du contenu est uploadé (wp-content/uploads); les répertoires où WordPress écrit potentiellement (cache, uploads, parfois des fichiers temporaires ou des extensions).

Le durcissement des permissions consiste à réduire le “surplus” de droits. Moins un processus peut lire ou écrire, moins il peut servir de vecteur.

Lire le langage des droits: propriétaire, groupe, monde

Les permissions UNIX suivent un modèle simple: propriétaire, groupe, autres (le “monde”). Chaque catégorie a des droits de lecture (r), écriture (w) et exécution (x). Sur un système web, l’exécution n’est pas uniquement “téléporter un binaire”. Pour les répertoires, l’exécution signifie “traverser”. Pour les fichiers, l’exécution a un sens particulier selon le serveur et le type de fichier.

Une règle pratique que je garde en tête: sur un serveur de production, le serveur web doit pouvoir lire ce qu’il sert, mais il ne doit pas écrire partout, et encore moins exécuter comme bon lui semble.

En général, sur un WordPress standard, le serveur web a besoin de:

    lire la plupart des fichiers PHP et ressources statiques; écrire dans les dossiers d’uploads et certains répertoires temporaires; créer et mettre à jour des fichiers selon les besoins du thème, des mises à jour WordPress, et des plugins.

Tout ce qui dépasse ces besoins peut être un risque. Un fichier de configuration lisible par “tout le monde” n’est pas forcément catastrophique sur un système bien configuré, mais ça enlève une couche de protection. Un dossier en écriture pour trop de monde, c’est un terrain favorable en cas de dépôt malveillant.

Le cas le plus fréquent: wp-config.php et l’exposition

Wp-config.php contient des éléments critiques, typiquement les paramètres de base de données. Selon la configuration du serveur, un fichier lisible ne veut pas dire accessible via HTTP. Mais il faut raisonner en défense en profondeur. Si, pour une raison quelconque, la configuration serveur n’est pas parfaite, des permissions laxistes peuvent transformer une exposition “discrète” en fuite directe.

Sur des environnements Linux classiques, on vise généralement un niveau où:

    seul l’utilisateur propriétaire (souvent l’utilisateur du processus web, parfois un compte dédié) peut lire; l’écriture pour wp-config.php est limitée, idéalement à personne sauf pendant une opération d’administration.

J’utilise souvent une approche empirique, sans dogme: je vérifie d’abord le contexte réel (qui exécute PHP, sous quel user, via quel mécanisme). Ensuite je durcis sans casser l’installation.

Comprendre PHP-FPM et mod_php, parce que les droits ne se traduisent pas pareil

Beaucoup de recommandations de “chmod 644 partout” circulent, mais elles ignorent le rôle exact de PHP. Avec mod_php, PHP tourne dans le même processus ou le même contexte que le serveur web. Avec PHP-FPM, le service PHP tourne sous un user défini (ou plusieurs piscines). Dans les deux cas, le principe est identique, mais la réalité du “qui a accès” change.

En pratique, j’ai vu des configurations où le bon modèle consistait à:

    donner au user PHP-FPM la lecture sur tout le code WordPress; lui donner l’écriture uniquement sur les répertoires nécessaires (uploads, parfois wp-content en fonction des workflows); empêcher l’écriture sur wp-includes, wp-admin, et la majeure partie de la racine.

Si vous durcissez “à l’aveugle”, vous risquez des erreurs en cascade: mises à jour qui échouent, médias qui ne s’uploadent plus, ou erreurs d’écriture pendant la génération de caches. Le durcissement doit être progressif, avec tests.

Attribuer le bon propriétaire et le bon groupe (le plus sous-estimé)

Avant même de parler de chmod, je commence par chown. Les permissions sans bon propriétaire peuvent créer une fausse impression de sécurité. Typiquement, si un répertoire a les bons modes mais appartient à un user inattendu, vous risquez qu’un autre processus, ou un compte d’administration trop large, puisse modifier.

Le bon duo propriétaire/groupe est déterminé par votre architecture: user du serveur web, user du service PHP, éventuellement un user CI/CD. Quand vous installez WordPress, pensez à la propriété dès le départ. Quand vous appliquez des correctifs, évitez les “chmod au hasard”. Il vaut mieux:

    connaître l’utilisateur qui exécute PHP; connaître l’utilisateur qui fait les uploads (souvent c’est PHP, pas un script “manuel”); aligner la propriété pour que le flux normal fonctionne.

Sur des serveurs multi-sites ou sur des environnements gérés par des panels (cPanel, Plesk ou équivalents), cette étape devient encore plus importante, parce que le panel peut réappliquer des permissions ou changer des propriétaires lors de certaines opérations.

La question qui fâche: uploads, plugins et thèmes, autoriser l’écriture ou non?

Les uploads sont un cas particulier. WordPress doit écrire des fichiers dans wp-content/uploads pour que les médias fonctionnent. Les plugins peuvent nécessiter des écritures (mise à jour automatique, génération de fichiers, parfois caches). Les thèmes peuvent générer des artefacts selon la logique interne.

Le durcissement réaliste consiste rarement à “interdire l’écriture partout”. Il consiste plutôt à compartimenter. Vous voulez que l’écriture soit possible uniquement dans des emplacements prévisibles. Le reste doit être en lecture seule pour le serveur web.

Une anecdote qui revient: sur un site “tout en FTP”, un ancien technicien avait donné l’écriture au mauvais dossier, pas seulement sur uploads, mais sur wp-content en général. Tout marchait, jusqu’au jour où un script indésirable s’est retrouvé quelque part qu’il ne fallait pas. Ce jour-là, le serveur a pu le servir côté PHP, pas seulement le stocker. La différence entre “stocké” et “exécuté” s’est faite grâce aux permissions et à la configuration de l’environnement. Réduire les droits d’écriture a ensuite limité l’impact: même si un dépôt arrivait, il n’avait plus le même pouvoir.

Vérifier avant de modifier: ce qui doit être observé

Je ne “durcis” jamais un serveur sans vérifier son état réel, parce que les permissions attendues varient selon votre stack. Le bon point de départ est de regarder les chemins sensibles et la configuration du service PHP.

Voici un premier tour, simple, utile, et souvent suffisant pour repérer les erreurs évidentes.

    Identifier l’utilisateur du processus PHP (PHP-FPM pools ou context mod_php). Contrôler la propriété et les permissions de wp-config.php, wp-content/uploads, et la racine WordPress. Vérifier que les répertoires destinés aux uploads sont les seuls à être largement en écriture. Repérer les fichiers PHP qui se trouvent dans des emplacements non “standards” (signe d’un dépôt). Tester l’upload d’un média et une mise à jour plugin après chaque changement majeur.

Cette étape ne dure pas longtemps, mais elle évite beaucoup d’aller-retour.

Modes de permissions: des repères réalistes, pas des recettes universelles

Quand on demande “quels chmod”, la réponse honnête est: ça dépend. La sécurité ne gagne pas sur le papier si, dans la pratique, votre serveur échoue à écrire où il doit. Il faut donc viser des repères défendables.

En général, sur un site WordPress:

    les fichiers PHP et les fichiers de code doivent être lisibles, rarement écrits par le serveur web; les répertoires doivent être traversables et, seulement pour certains, modifiables; l’écriture “monde” est à éviter; la lecture “monde” pour les fichiers sensibles est à limiter quand c’est possible.

Une façon de raisonner que j’utilise: si le serveur web n’a pas besoin d’écrire dans un répertoire, on retire le bit d’écriture. Si on ne retire pas, on au moins s’assure que l’accès est limité au bon user et au bon groupe.

Pour donner des repères sans faire semblant d’un standard unique, on voit souvent des schémas du type:

    fichiers: lecture pour le propriétaire et le groupe, aucune écriture publique; répertoires: traversable pour le serveur web, écriture limitée aux dossiers d’uploads et apparentés.

Les valeurs exactes (par exemple 640, 644, 750, 755, etc.) Varient selon votre système, vos utilisateurs et vos contraintes. Ce qui compte vraiment, c’est la logique: lecture pour servir, écriture seulement là où WordPress produit du contenu ou met à jour.

Durcir sans casser: l’approche “zones” sur WordPress

J’aime découper WordPress en zones fonctionnelles. Pas dans une grille abstraite, mais dans la réalité des flux.

Zone code: wp-admin, wp-includes, fichiers racine du code, et la plupart de wp-content hors uploads. Ici, l’écriture doit être strictement limitée au mécanisme de déploiement ou aux opérations d’administration. Zone contenu: wp-content/uploads. Ici, l’écriture est nécessaire. L’objectif est de limiter qui peut exécuter du PHP et qui peut déposer des fichiers. Zone runtime: caches, fichiers temporaires, variations selon les plugins. Certains plugins écrivent ailleurs que uploads. C’est là que les erreurs arrivent, parce que l’on durcit “en supposant” que tout se passe uniquement dans uploads.

Le durcissement des permissions PHP marche quand on accepte une discipline: après chaque changement, on teste les opérations réelles, upload, navigation, génération de pages, et au minimum la mise à jour d’un plugin en environnement de préproduction.

Réduire la surface PHP: ce que la permission ne suffit pas à faire

Une mise en garde importante: régler les permissions ne remplace pas la configuration du serveur. Un fichier peut être lisible mais non exécutable selon la règle du serveur, ou exécutable si la règle correspond. C’est pourquoi il faut souvent combiner permissions et règles serveur.

L’objectif: même si un fichier PHP est déposé, il ne doit pas pouvoir être exécuté depuis des emplacements non prévus. Sur beaucoup d’installations, on peut protéger les uploads en empêchant l’exécution PHP dans wp-content/uploads. Les options dépendront de votre serveur (Apache, Nginx, et votre stratégie PHP-FPM).

Si vous ne pouvez pas modifier la configuration serveur (hébergement mutualisé très contraint), alors les permissions deviennent d’autant plus critiques pour empêcher la lecture ou l’écriture non souhaitée.

Une matrice simple pour les dossiers sensibles

Sans vous enfermer dans un “chmod magique”, vous pouvez viser une cohérence. Je propose une matrice pratique, à adapter à votre user PHP-FPM, mais qui donne un cadre.

| Emplacement | Intention sécurité | Typiquement côté permissions | |---|---|---| | wp-config.php | secret, peu exposé | lecture limitée au propriétaire/groupe, écriture fortement limitée | | wp-content/uploads | dépôt nécessaire, exécution évitée | écriture pour le mécanisme web, lecture possible, exécution PHP bloquée via config si possible | | wp-includes et wp-admin | code stable | lecture pour le serveur, écriture limitée | | fichiers racine WordPress | code et intégrité | lecture, écriture limitée aux opérations d’administration | | dossiers cache générés | runtime, contrôlé | écriture nécessaire, permissions resserrées selon le plugin utilisé |

Le mot clé ici est cohérence. Si vos caches ou dossiers de plugins écrivent quelque part, et que vous durcissez sans tenir compte de ces emplacements, vous allez créer des erreurs et parfois pousser l’équipe à “relâcher” de nouveau des droits.

Cas limites: multisite, staging, et déploiements automatisés

Les environnements WordPress modernes ne sont pas tous identiques. En multisite, les chemins peuvent varier, et les uploads peuvent être gérés différemment. En staging, on change souvent d’utilisateurs, et les déploiements CI/CD peuvent déposer des fichiers avec des propriétaires différents.

Si votre pipeline déploie en chown/chmod après coup, vous pouvez durcir pour la production, puis perdre le durcissement lors d’un prochain déploiement. C’est un point que j’ai vu provoquer des “régressions invisibles”: après une mise à jour, les droits reviennent à un état plus large, pas assez pour provoquer une panne, mais assez pour baisser le niveau de sécurité.

Le remède n’est pas d’éviter les automatisations. Le remède, c’est de rendre le durcissement reproductible. Documentez vos objectifs de permissions, et intégrez-les dans le process de déploiement.

Méthode de durcissement recommandée, en pratique

Je passe généralement par une séquence qui limite le risque de panne et maximise l’apprentissage:

    d’abord, on observe l’état actuel, on liste les permissions réellement en place; ensuite, on durcit la zone la plus “sensible au secret”, wp-config.php et les fichiers racine; puis on resserre la zone code (wp-admin, wp-includes), en gardant un œil sur les mises à jour; enfin, on contrôle les dossiers d’uploads et leurs permissions, puis on vérifie que l’exécution PHP n’y est pas possible selon la config serveur; on teste upload, affichage, mise à jour plugin, et si possible génération de page et caches.

Ce que je cherche, c’est la https://gardewp.fr/securite-wordpress/ stabilité. La sécurité qui casse le site devient une charge, et une charge finit toujours par être contournée, souvent par relaxation des permissions.

Surveiller après durcissement: les permissions sont un état, pas un décor

Une fois que vous avez durci, il faut surveiller. Les permissions se modifient facilement via:

    mises à jour WordPress; plugins qui écrivent des fichiers; opérations FTP ou SFTP faites par des humains; déploiements via des scripts.

L’idéal est d’avoir une vérification régulière, au moins mensuelle au départ, puis ajustée selon la fréquence de déploiement. En production, je conseille de traiter la “permission drift” comme on traite les dérives de configuration: c’est silencieux jusqu’au jour où ça ne l’est plus.

Une pratique utile consiste à comparer les permissions “attendues” et les permissions réelles, au moins sur les chemins sensibles. Si un plugin change l’état sur un dossier inattendu, vous le saurez tôt.

Réponses aux erreurs courantes (et comment éviter de revenir en arrière)

Quand on durcit les droits, les erreurs typiques que vous pouvez voir sont:

    échec d’upload des médias, messages d’autorisation; impossibilité de mettre à jour un plugin ou un thème; génération de pages qui échoue si un plugin doit écrire dans un dossier que vous avez rendu trop strict.

Le réflexe que je déconseille est de “rétablir large” pour que ça marche. C’est tentant, et je l’ai vu arriver. La meilleure approche est de remonter à l’emplacement exact dont l’écriture est refusée, puis d’accorder l’accès minimal nécessaire à ce répertoire.

Si vous devez choisir entre “trop large” et “trop strict”, choisissez trop strict dans un premier temps, et ajustez en ciblant le dossier précis. C’est plus lent au départ, mais c’est plus propre sur la durée.

Sécurité globale: intégrer les permissions dans une stratégie cohérente

Les permissions ne sont qu’une partie du puzzle. Leur valeur vient quand elles s’insèrent dans une stratégie plus large:

    mots de passe solides, et principe du moindre privilège pour les comptes administrateurs; mises à jour régulières, surtout sur les plugins à haut risque; limitation de l’accès au fichier wp-config.php et sécurisation d’un minimum d’entrées; durcissement serveur (désactivation exécution PHP dans les emplacements non prévus, restrictions d’accès, logs).

La logique est la même que pour la maison: verrouiller les portes ne remplace pas d’avoir une alarme, mais une porte verrouillée évite bien des incidents.

Derniers conseils, ceux que je donnerais avant d’appliquer vos changements

Si vous voulez un critère simple avant de modifier: faites en sorte que le serveur web puisse faire son travail normal, et rien de plus.

Pour la sécurité, j’insiste toujours sur deux vérifications:

Les fichiers sensibles comme wp-config.php doivent rester sous contrôle étroit. Les dossiers où du PHP pourrait être déposé ne doivent pas permettre l’exécution, ou alors doivent rester impossibles à modifier hors flux d’administration.

Si vous avez un doute sur un plugin qui écrit dans des emplacements “bizarres”, ne corrigez pas en généralité. Cherchez l’emplacement exact, observez les besoins, puis donnez uniquement ce qu’il faut.

image

La protection WordPress, même quand elle commence par une histoire de permissions PHP, devient rapidement une question de discipline opérationnelle. Une fois que vous tenez cette discipline, la sécurité se renforce sans vous enfermer dans un système fragile.