hardeningmysqlpostgresql
MDstable
Hardening bases de données — MySQL & PostgreSQL
Sécuriser MySQL et PostgreSQL : comptes, réseau, audit, chiffrement, backups
snippetintermediate 2025-05-14 7 min read
hardeningmysqlpostgresqldatabasesecurityencryptionauditsql
MySQL / MariaDB
Installation initiale
bash
# Premier lancement obligatoire — supprime les défauts dangereuxmysql_secure_installation# Répond OUI à tout :# - Set root password# - Remove anonymous users# - Disallow root login remotely# - Remove test database# - Reload privilege tables
sql
-- Nettoyage manuel si mysql_secure_installation n'a pas tout couvert-- Supprimer les comptes anonymesDELETE FROM mysql.user WHERE User='';-- Supprimer la base de testDROP DATABASE IF EXISTS test;DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%';-- Restreindre root à localhost uniquementDELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');-- Vérifier les comptes existantsSELECT User, Host, plugin, authentication_string FROM mysql.user;-- Appliquer les changementsFLUSH PRIVILEGES;
Comptes applicatifs dédiés
sql
Variables
{{STRONG_PASSWORD}}
{{APP_SERVER_IP}}
-- Principe : un compte par application, privilèges minimaux-- Ne jamais utiliser root pour les connexions applicatives-- Créer un compte applicatifCREATE USER 'app'@'localhost' IDENTIFIED BY '{{STRONG_PASSWORD}}';-- Accorder uniquement les privilèges nécessaires (pas GRANT OPTION, pas SUPER)GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO 'app'@'localhost';-- Pour une app read-only (reporting)CREATE USER 'app_ro'@'{{APP_SERVER_IP}}' IDENTIFIED BY '{{STRONG_PASSWORD}}';GRANT SELECT ON appdb.* TO 'app_ro'@'{{APP_SERVER_IP}}';-- Vérifier les privilèges accordésSHOW GRANTS FOR 'app'@'localhost';FLUSH PRIVILEGES;
Configuration /etc/mysql/mysql.conf.d/mysqld.cnf
bash
Variables
{{INTERNAL_IP}}
mysqld# Réseau — restreindre l'écoutebind-address 127001 # ou IP interne uniquement# bind-address = {{INTERNAL_IP}}# Désactiver la résolution DNS (performance + évite rebinding attacks)skip-name-resolve 1# Désactiver LOCAL INFILE (lecture de fichiers locaux via SQL)local_infile 0secure_file_priv /var/lib/mysql-files # restreindre le répertoire d'import/export# Logslog_error /var/log/mysql/error.loggeneral_log 0 # désactivé en prod (verbeux)slow_query_log 1slow_query_log_file /var/log/mysql/slow.loglong_query_time 2# TLS — forcer les connexions chiffréesrequire_secure_transport ONssl-ca /etc/mysql/ssl/ca.pemssl-cert /etc/mysql/ssl/server-cert.pemssl-key /etc/mysql/ssl/server-key.pem# Chiffrement at-rest (InnoDB)innodb_encrypt_tables ONinnodb_encrypt_log ONearly-plugin-load keyring_filesokeyring_file_data /var/lib/mysql-keyring/keyring
sql
Variables
{{APP_SERVER_IP}}
{{MGMT_IP}}
-- Vérifier que TLS est actifSHOW VARIABLES LIKE '%ssl%';SHOW STATUS LIKE 'Ssl_cipher';-- Forcer TLS pour un compte spécifiqueALTER USER 'app'@'{{APP_SERVER_IP}}' REQUIRE SSL;-- Forcer TLS + certificat clientALTER USER 'admin'@'{{MGMT_IP}}' REQUIRE X509;
Audit Plugin (MySQL Enterprise / MariaDB)
sql
-- MariaDB Audit PluginINSTALL PLUGIN server_audit SONAME 'server_audit.so';-- Configuration (my.cnf)-- server_audit_logging = ON-- server_audit_events = CONNECT,QUERY,TABLE-- server_audit_file_path = /var/log/mysql/audit.log-- server_audit_file_rotate_size = 1073741824-- MySQL Enterprise AuditINSTALL PLUGIN audit_log SONAME 'audit_log.so';SET GLOBAL audit_log_policy = 'ALL';
⚠ Attention —
general_log = 1 logue absolument toutes les requêtes SQL, y compris les mots de passe en clair si passés dans des requêtes. Ne l'activer qu'en environnement de debug isolé, jamais en production. Utiliser le Audit Plugin pour un audit de production.
PostgreSQL
pg_hba.conf — Authentification
bash
Variables
{{VERSION}}
{{APP_IP}}
{{REPORT_IP}}
# /etc/postgresql/{{VERSION}}/main/pg_hba.conf# TYPE DATABASE USER ADDRESS METHOD# Connexions locales via socketlocal all postgres peer # OS user postgres uniquementlocal all all scram-sha-256 # comptes applicatifs# Connexions réseauhost appdb app_user {{APP_IP}}/32 scram-sha-256host appdb app_ro {{REPORT_IP}}/32 scram-sha-256# Refuser tout le restehost all all 0000/0 reject# SUPPRIMER ces lignes dangereuses si présentes :# host all all 0.0.0.0/0 trust ← pas d'authentification# host all all 0.0.0.0/0 md5 ← MD5 obsolète, vulnérable
postgresql.conf — Configuration sécurisée
bash
Variables
{{VERSION}}
{{INTERNAL_IP}}
# /etc/postgresql/{{VERSION}}/main/postgresql.conf# Réseaulisten_addresses 'localhost' # ou '{{INTERNAL_IP}}'port 5432 # changer si possible# SSLssl onssl_cert_file '/etc/ssl/certs/server.crt'ssl_key_file '/etc/ssl/private/server.key'ssl_ca_file '/etc/ssl/certs/ca.crt'ssl_min_protocol_version 'TLSv1.2'# Logs d'auditlog_connections onlog_disconnections onlog_failed_auth onlog_statement 'ddl' # DDL en prod (ALTER, DROP, CREATE)# log_statement = 'all' # tout (debug uniquement)log_duration onlog_min_duration_statement 1000 # requêtes > 1 seconde# Sécuritépassword_encryption scram-sha-256
Rôles et permissions PostgreSQL
sql
Variables
{{STRONG_PASSWORD}}
-- Créer un rôle applicatif avec privilèges minimauxCREATE ROLE app_user LOGIN PASSWORD '{{STRONG_PASSWORD}}';-- Connexion à la base de donnéesGRANT CONNECT ON DATABASE appdb TO app_user;-- Accès au schémaGRANT USAGE ON SCHEMA public TO app_user;-- Privilèges sur les tablesGRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_user;GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO app_user;-- Appliquer aux futures tables (DEFAULT PRIVILEGES)ALTER DEFAULT PRIVILEGES IN SCHEMA publicGRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_user;ALTER DEFAULT PRIVILEGES IN SCHEMA publicGRANT USAGE, SELECT ON SEQUENCES TO app_user;-- Retirer le privilège CREATE du schéma public pour PUBLICREVOKE CREATE ON SCHEMA public FROM PUBLIC;-- Désactiver la connexion distante pour le superuser postgresALTER ROLE postgres NOLOGIN;-- Pour se connecter : sudo -u postgres psql (via peer auth locale)
sql
Variables
{{TENANT_UUID}}
-- Row Level Security (RLS) — isolation des données par locataireALTER TABLE orders ENABLE ROW LEVEL SECURITY;CREATE POLICY orders_tenant_isolation ON ordersFOR ALLTO app_userUSING (tenant_id = current_setting('app.tenant_id')::uuid);-- L'application doit positionner le contexte avant chaque requête :-- SET app.tenant_id = '{{TENANT_UUID}}';
pgaudit — Audit complet
sql
-- Installation de pgaudit-- Dans postgresql.conf :-- shared_preload_libraries = 'pgaudit'-- Configuration (postgresql.conf)-- pgaudit.log = 'write, ddl, role, connection'-- pgaudit.log_catalog = off-- pgaudit.log_parameter = on-- pgaudit.log_statement_once = on-- Vérifier que pgaudit est chargéSELECT * FROM pg_extension WHERE extname = 'pgaudit';-- Activer l'audit sur une base spécifiqueALTER DATABASE appdb SET pgaudit.log = 'write, ddl, role';
Principes communs MySQL & PostgreSQL
bash
Variables
{{APP_SERVER_IP}}
# Ne jamais exposer les ports DB sur Internet# Port 3306 (MySQL) et 5432 (PostgreSQL) → firewall# iptables — bloquer l'accès externeiptables -A INPUT -p tcp --dport 3306 -s {{APP_SERVER_IP}} -j ACCEPTiptables -A INPUT -p tcp --dport 3306 -j DROPiptables -A INPUT -p tcp --dport 5432 -s {{APP_SERVER_IP}} -j ACCEPTiptables -A INPUT -p tcp --dport 5432 -j DROP# Vérifier les ports en écoutess -tulnp | grep -E "3306|5432"# Doit afficher 127.0.0.1:PORT ou IP_INTERNE:PORT, jamais 0.0.0.0:PORT
bash
# Backups chiffrés avec vérification d'intégrité# MySQL — backup chiffré via GPGmysqldump --single-transaction --routines --triggers appdb |gzip |gpg --batch --yes --passphrase-file /root/.backup-passphrase--symmetric --cipher-algo AES256> /backup/appdb_date Ymdsqlgzgpg# PostgreSQL — backup chiffrépg_dump appdb |gzip |gpg --batch --yes --passphrase-file /root/.backup-passphrase--symmetric --cipher-algo AES256> /backup/appdb_date Ymddumpgzgpg# Vérifier l'intégritésha256sum /backup/appdb_date Ymdgpg > /backup/checksums.sha256# Stocker les checksums hors du serveur de backup# Test de restauration mensuel obligatoiregpg --decrypt /backup/appdb_latest.dump.gz.gpg | gunzip | psql -U postgres test_restore_db
bash
Variables
{{DB_SERVER}}
{{BASTION_USER}}
{{BASTION_IP}}
{{DB_USER}}
# Accès administrateur via bastion host uniquement# Depuis le bastion :ssh -L 3306{{DB_SERVER}}3306 {{BASTION_USER}}{{BASTION_IP}}mysql -h 127001 -P 3306 -u admin -p# Ou SSH ProxyJumpssh -J {{BASTION_USER}}{{BASTION_IP}} -L 5432{{DB_SERVER}}5432 {{DB_USER}}{{DB_SERVER}}psql -h 127001 -p 5432 -U admin appdb
Checklist hardening base de données
Checklist0/12
💡 Tip —
Ne jamais utiliser root (MySQL) ou postgres (PostgreSQL) comme compte applicatif — si l'application est compromise via injection SQL ou RCE, l'attaquant obtient un accès complet à toutes les bases de données du serveur, peut lire les fichiers système (LOAD DATA INFILE), et créer des backdoors. Un compte applicatif limité à SELECT, INSERT, UPDATE, DELETE sur une seule base contient la compromission.
OPS·BRAIN v1.075 notes · Securitylocal