Un audit de sécurité de routine sur un serveur mutualisé hébergeant vingt-deux sites WordPress clients a révélé un chiffre qui a fait tiquer toute l’équipe : quatorze des vingt-deux utilisateurs MySQL configurés disposaient d’un accès en lecture-écriture sur l’ensemble des bases du serveur, alors qu’aucun d’eux n’avait légitimement besoin d’accéder à autre chose que la base de son propre site.
Cette check-list ne traite pas de l’isolation des processus PHP-FPM, sujet distinct déjà couvert séparément. Elle détaille la remise à plat des permissions MySQL sur un serveur mutualisé existant, base par base, sans réinstallation ni migration, applicable sur un serveur en production sans interruption de service pour les sites concernés.
Comprendre l’origine du problème
Ces droits trop larges ne résultent presque jamais d’une décision volontaire. Ils s’accumulent au fil du temps : un utilisateur créé rapidement avec GRANT ALL PRIVILEGES ON *.* TO 'client'@'localhost' pour dépanner un import de base un soir pressé, jamais révoqué ensuite ; un script de provisioning ancien qui créait systématiquement des comptes avec des droits globaux par simplicité, avant que l’équipe ne standardise sur des droits restreints par base.
Le risque concret : si l’un de ces vingt-deux sites WordPress est compromis via une faille applicative (plugin vulnérable, thème obsolète), l’attaquant qui obtient les identifiants de base de données stockés dans wp-config.php peut, avec un utilisateur aux droits globaux, accéder également aux vingt et une autres bases du serveur, alors qu’un utilisateur correctement restreint ne lui aurait donné accès qu’à une seule.

Étape 1 — Cartographier l’existant avant toute modification
La première étape, indispensable, consiste à lister précisément qui a accès à quoi avant de toucher à quoi que ce soit. La commande SHOW GRANTS FOR 'utilisateur'@'localhost'; exécutée pour chaque utilisateur permet de dresser cette cartographie exacte. Sur MySQL 8 comme sur MariaDB, la table mysql.db et la vue information_schema.SCHEMA_PRIVILEGES permettent également une requête globale plus rapide pour repérer d’un coup tous les privilèges au niveau serveur (*.*) attribués à chaque compte.
Étape 2 — Créer des comptes restreints en parallèle
Plutôt que de modifier directement les droits d’un utilisateur en production — ce qui coupe l’accès au site le temps de la transition — la méthode la plus sûre consiste à créer un nouvel utilisateur restreint, à basculer wp-config.php vers ce nouvel utilisateur, à vérifier que le site fonctionne normalement, puis seulement ensuite à révoquer et supprimer l’ancien compte trop permissif.
CREATE USER 'client_isole'@'localhost' IDENTIFIED BY 'mot-de-passe-genere';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, INDEX
ON client_wp.* TO 'client_isole'@'localhost';
FLUSH PRIVILEGES;
Ce jeu de privilèges couvre l’ensemble des opérations dont WordPress a réellement besoin au quotidien (lecture, écriture, création de tables lors d’une migration de plugin, modification de structure) sans jamais octroyer d’accès aux autres bases du serveur ni de privilèges d’administration globale comme SUPER ou GRANT OPTION.
Étape 3 — Basculer et vérifier sans interruption
- Mettre à jour les constantes
DB_USERetDB_PASSWORDdanswp-config.php. - Vérifier immédiatement l’accès au site et à l’administration WordPress.
- Tester une opération d’écriture réelle (publication d’un brouillon, mise à jour d’une option) pour confirmer les droits
INSERTetUPDATE. - Tester une opération de migration si un plugin est prévu prochainement, pour confirmer les droits
ALTERetCREATE.
Étape 4 — Révoquer proprement l’ancien compte
Une fois le nouveau compte validé sur plusieurs jours d’utilisation normale, l’ancien utilisateur aux droits globaux est révoqué puis supprimé : REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'client'@'localhost'; DROP USER 'client'@'localhost';. Cette étape est systématiquement reportée de quelques jours par précaution, le temps de s’assurer qu’aucun script de sauvegarde ou de supervision oublié n’utilisait encore cet ancien compte.
Un droit accordé « pour dépanner un soir » devient, en pratique, un droit permanent tant que personne ne prend explicitement le temps de le révoquer. La remise à plat régulière n’est pas un luxe, c’est un entretien de base.
Notre check-list finale
Sur ce serveur de vingt-deux sites, la remise à plat complète a pris deux journées de travail réparties sur une semaine, sans aucune interruption de service constatée par les clients. La documentation officielle de MySQL sur le système de privilèges reste la référence à consulter en cas de doute sur la portée exacte d’un privilège donné : dev.mysql.com/doc/refman.