Installer et sécuriser phpMyAdmin derrière Nginx sur Debian 12

Optimisé pour un environnement de production léger (Debian 12 Bookworm, Nginx, PHP‑FPM, MariaDB/MySQL). Convient aussi à un lab. Inclut durcissement, automatisation et dépannage.
1. Pré‑requis
- Debian 12 (Bookworm) avec accès sudo.
- Nginx installé (sinon, voir plus bas).
- Ports 80 et 443 ouverts.
- Nom de domaine pointant vers le serveur (recommandé pour HTTPS).
- Astuce : si plusieurs versions de PHP sont installées, identifiez la version active avec la commande suivante.
php -v
# récupère 8.x.y → notez 8.2 ou 8.1, etc.
2. Mise à jour du système
Apadate : apt update + correctifs
sudo apt update && sudo apt -y full-upgrade
sudo reboot
3. Installer Nginx, PHP‑FPM et extensions
sudo apt install -y nginx php-fpm php-cli php-mbstring php-xml php-zip php-curl php-mysql php-gd php-intl php-bcmath php-json
sudo apt install -y php-imagick php-apcu
Vérifier le socket PHP‑FPM (remplacez 8.2 par votre version si besoin) :
ls -l /run/php/ | grep fpm
# Exemple: /run/php/php8.2-fpm.sock
4. Installer MariaDB/MySQL et sécuriser
sudo apt install -y mariadb-server mariadb-client
sudo mysql_secure_installation
Recommandations pendant l’assistant :
- Définir un mot de passe root SQL (si non défini).
- Supprimer utilisateurs anonymes.
- Désactiver le login root distant.
- Supprimer la base de test.
- Recharger les privilèges.
5. Créer un utilisateur DB dédié à phpMyAdmin
phpMyAdmin n’a pas besoin d’un compte root. Créez un compte admin limité.
sudo mysql
Dans le shell SQL :
CREATE USER 'pma_admin'@'localhost' IDENTIFIED BY 'motdepasse-solideIci!';
GRANT ALL PRIVILEGES ON *.* TO 'pma_admin'@'localhost' WITH GRANT OPTION;
FLUSH PRIVILEGES;
EXIT;
6. Installer phpMyAdmin (2 méthodes)
6.A : via apt (simple)
sudo apt install -y phpmyadmin
Pendant l’installation :
- Choisir nginx: Non (il ne sera pas configuré automatiquement) → la configuration Nginx se fera à la main.
- Choisir dbconfig-common: Oui et définir le mot de passe de l’utilisateur phpMyAdmin pma (différent de pma_admin ci‑dessus).
Les fichiers sont généralement dans /usr/share/phpmyadmin.
6.B : via archive officielle (plus à jour)
cd /var/www
sudo mkdir -p pma && cd pma
# Téléchargez la dernière version depuis https://www.phpmyadmin.net/downloads/
# Exemple :
wget https://files.phpmyadmin.net/phpMyAdmin/5.2.1/phpMyAdmin-5.2.1-all-languages.tar.gz
sudo tar xzf phpMyAdmin-*-all-languages.tar.gz
sudo mv phpMyAdmin-*-all-languages phpmyadmin
sudo chown -R www-data:www-data /var/www/pma/phpmyadmin
Créer le fichier de config si besoin :
sudo -u www-data cp /var/www/pma/phpmyadmin/config.sample.inc.php /var/www/pma/phpmyadmin/config.inc.php
sudo -u www-data nano /var/www/pma/phpmyadmin/config.inc.php
Dans config.inc.php, générer un blowfish_secret de 32 caractères aléatoires :
<?php
$cfg['blowfish_secret'] = 'MettezIciUnePhraseSecrèteLongueEtUnique!!!!';
7. Configurer Nginx : alias ou sous‑domaine dédié
Deux approches : /phpmyadmin comme alias sur un vhost existant, ou pma.domaine.tld dédié. La seconde facilite les ACL et le rate‑limit.
Option A — Alias /phpmyadmin sur un vhost existant
Dans votre bloc server, par exemple /etc/nginx/sites-available/mon-site.conf :
location /phpmyadmin {
alias /usr/share/phpmyadmin; # ou /var/www/pma/phpmyadmin si méthode 6.B
index index.php;
try_files $uri $uri/ /phpmyadmin/index.php?$args;
}
location ~ ^/phpmyadmin/(.+\.php)$ {
alias /usr/share/phpmyadmin; # alignez avec votre chemin réel
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_pass unix:/run/php/php8.2-fpm.sock; # ajustez la version
fastcgi_read_timeout 120s;
}
location ~* ^/phpmyadmin/(.+\.(?:png|jpg|jpeg|gif|css|js|ico|html|svg))$ {
alias /usr/share/phpmyadmin; # alignez avec votre chemin réel
access_log off;
log_not_found off;
expires 7d;
}
Option B — Vhost dédié pma.domaine.tld
Créer le fichier /etc/nginx/sites-available/pma.conf :
server {
listen 80;
server_name pma.example.com;
root /usr/share/phpmyadmin; # ou /var/www/pma/phpmyadmin
index index.php index.html;
return 301 https://$host$request_uri; # redirige vers HTTPS après Certbot
}
server {
listen 443 ssl http2;
server_name pma.example.com;
ssl_certificate /etc/letsencrypt/live/pma.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/pma.example.com/privkey.pem;
root /usr/share/phpmyadmin; # ou /var/www/pma/phpmyadmin
index index.php;
add_header X-Frame-Options SAMEORIGIN always;
add_header X-Content-Type-Options nosniff always;
add_header Referrer-Policy no-referrer-when-downgrade always;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php8.2-fpm.sock; # ajustez la version
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
fastcgi_read_timeout 120s;
}
location ~* \.(?:css|js|ico|gif|jpe?g|png|svg)$ {
access_log off;
expires 7d;
}
}
Activer et tester :
sudo ln -s /etc/nginx/sites-available/pma.conf /etc/nginx/sites-enabled/pma.conf
sudo nginx -t && sudo systemctl reload nginx
8. Activer HTTPS avec Let’s Encrypt
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d pma.example.com
# Si alias: certbot --nginx -d mon-site.example.com
Vérifier le renouvellement automatique :
systemctl status certbot.timer
9. Durcissement de phpMyAdmin
- Désactiver l’accès à /setup (deprecated). Exemple alias :
location ^~ /phpmyadmin/setup { deny all; }
Et si vhost dédié :
location ^~ /setup { deny all; }
- Blowfish secret unique et long (voir plus haut).
- Désactiver AllowArbitraryServer pour éviter les risques SSRF. Dans config.inc.php :
<?php
$cfg['AllowArbitraryServer'] = false;
- Limiter aux connexions locales DB.
- Désactiver le login root via réseau et préférer sudo mysql.
10. Ajuster PHP (upload, temps d’exécution)
Éditer le fichier /etc/php/8.2/fpm/php.ini (adaptez la version) :
upload_max_filesize = 512M
post_max_size = 512M
max_execution_time = 300
memory_limit = 512M
Puis redémarrer PHP‑FPM :
sudo systemctl restart php8.2-fpm
11. Optimisations (Opcache/APCu)
Dans /etc/php/8.2/fpm/php.ini :
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=192
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=100000
opcache.validate_timestamps=1
opcache.revalidate_freq=2
APCu (si installé) dans /etc/php/8.2/mods-available/apcu.ini :
apc.enabled=1
apc.shm_size=128M
12. Limiter l’accès : IP allowlist et HTTP Auth
IP Allowlist (exemple alias /phpmyadmin)
location /phpmyadmin {
allow 203.0.113.10; # votre IP fixe
allow 2001:db8::/48; # votre préfixe IPv6
deny all;
alias /usr/share/phpmyadmin;
index index.php;
}
HTTP Auth (Basic)
sudo apt install -y apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd-pma admin
Ajouter dans le vhost :
location /phpmyadmin {
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd-pma;
alias /usr/share/phpmyadmin;
index index.php;
try_files $uri $uri/ /phpmyadmin/index.php?$args;
}
Combiner IP allowlist et HTTP Auth pour un meilleur niveau de sécurité.
13. Rate‑limit et Fail2ban
Rate‑limit Nginx
Dans le contexte http, par exemple /etc/nginx/nginx.conf :
limit_req_zone $binary_remote_addr zone=pma_limit:10m rate=5r/s;
Dans le bloc server :
location /phpmyadmin/ {
limit_req zone=pma_limit burst=20 nodelay;
}
Fail2ban (filtre Nginx ciblé)
sudo apt install -y fail2ban
sudo tee /etc/fail2ban/filter.d/nginx-phpmyadmin.conf >/dev/null <<'EOF'
[Definition]
failregex = ^<HOST> - .* "(GET|POST) /phpmyadmin.*" .* (401|403) .*$
ignoreregex =
EOF
sudo tee /etc/fail2ban/jail.d/nginx-phpmyadmin.local >/dev/null <<'EOF'
[nginx-phpmyadmin]
enabled = true
port = http,https
filter = nginx-phpmyadmin
logpath = /var/log/nginx/access.log
maxretry = 8
findtime = 10m
bantime = 1h
EOF
sudo systemctl restart fail2ban
sudo fail2ban-client status nginx-phpmyadmin
14. Sauvegardes et mises à jour
- Sauvegardes de configuration : /etc/nginx/, /etc/php/*/fpm/, /etc/phpmyadmin/ ou /var/www/pma/phpmyadmin/.
- Mises à jour classiques :
sudo apt update && sudo apt -y upgrade
- Méthode archive officielle (6.B) : retélécharger la nouvelle archive et remplacer le dossier.
- Réaliser un snapshot VM ou conteneur avant une grosse modification.
15. Tests et Dépannage
- Tester Nginx :
sudo nginx -t
sudo systemctl reload nginx
- Logs : /var/log/nginx/access.log, /var/log/nginx/error.log, journalctl -u php8.2-fpm.
- PHP‑FPM down : vérifier le socket /run/php/php8.2-fpm.sock ou passer en fastcgi_pass 127.0.0.1:9000 si vous utilisez TCP.
- Erreur 403 sur alias : vérifier alias ou root, try_files et droits www-data.
- Timeout import : augmenter client_max_body_size dans le bloc server et fastcgi_read_timeout.
- CSRF token invalide : vérifier cookies, blowfish_secret, et l’horloge système (timedatectl).
16. Annexes utiles
Snippet sécurité Nginx (headers)
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
# CSP minimale (testez vos assets avant de l’appliquer en prod)
add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' data: blob: https:;" always;
Tuning PHP‑FPM (pool)
Fichier /etc/php/8.2/fpm/pool.d/www.conf :
pm = dynamic
pm.max_children = 32
pm.start_servers = 4
pm.min_spare_servers = 4
pm.max_spare_servers = 16
pm.max_requests = 500
Adapter selon la RAM et la charge.
UFW (pare‑feu simple)
sudo apt install -y ufw
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw status
Nettoyage de l’alias par défaut Debian
Si vous avez installé via apt, Debian peut créer une configuration Apache. Pour Nginx, ignorez‑la et maintenez vos vhosts dans /etc/nginx/sites-available/.
17. Check‑list finale
- apt update et upgrade effectués
- Nginx et PHP‑FPM opérationnels (page phpinfo de test)
- MariaDB sécurisé avec mysql_secure_installation
- phpMyAdmin installé (apt ou archive) et accessible
- HTTPS actif (Let’s Encrypt)
- ACL (IP et/ou HTTP Auth) en place
- Rate‑limit et Fail2ban actifs
- Sauvegardes prévues et logs surveillés
Fin du tutoriel. Besoin d’une variante adaptée à ton domaine, à ta version de PHP exacte ou d’un script Bash ou Ansible idempotent ?


















