MDstable
NoteSnippetChecklistPlaybook

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 dangereux
mysql_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 anonymes
DELETE FROM mysql.user WHERE User='';
-- Supprimer la base de test
DROP DATABASE IF EXISTS test;
DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%';
-- Restreindre root à localhost uniquement
DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');
-- Vérifier les comptes existants
SELECT User, Host, plugin, authentication_string FROM mysql.user;
-- Appliquer les changements
FLUSH 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 applicatif
CREATE 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és
SHOW GRANTS FOR 'app'@'localhost';
FLUSH PRIVILEGES;

Configuration /etc/mysql/mysql.conf.d/mysqld.cnf

bash
Variables
{{INTERNAL_IP}}
mysqld
# Réseau — restreindre l'écoute
bind-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 0
secure_file_priv /var/lib/mysql-files # restreindre le répertoire d'import/export
# Logs
log_error /var/log/mysql/error.log
general_log 0 # désactivé en prod (verbeux)
slow_query_log 1
slow_query_log_file /var/log/mysql/slow.log
long_query_time 2
# TLS — forcer les connexions chiffrées
require_secure_transport ON
ssl-ca /etc/mysql/ssl/ca.pem
ssl-cert /etc/mysql/ssl/server-cert.pem
ssl-key /etc/mysql/ssl/server-key.pem
# Chiffrement at-rest (InnoDB)
innodb_encrypt_tables ON
innodb_encrypt_log ON
early-plugin-load keyring_fileso
keyring_file_data /var/lib/mysql-keyring/keyring
sql
Variables
{{APP_SERVER_IP}}
{{MGMT_IP}}
-- Vérifier que TLS est actif
SHOW VARIABLES LIKE '%ssl%';
SHOW STATUS LIKE 'Ssl_cipher';
-- Forcer TLS pour un compte spécifique
ALTER USER 'app'@'{{APP_SERVER_IP}}' REQUIRE SSL;
-- Forcer TLS + certificat client
ALTER USER 'admin'@'{{MGMT_IP}}' REQUIRE X509;

Audit Plugin (MySQL Enterprise / MariaDB)

sql
-- MariaDB Audit Plugin
INSTALL 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 Audit
INSTALL 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 socket
local all postgres peer # OS user postgres uniquement
local all all scram-sha-256 # comptes applicatifs
# Connexions réseau
host appdb app_user {{APP_IP}}/32 scram-sha-256
host appdb app_ro {{REPORT_IP}}/32 scram-sha-256
# Refuser tout le reste
host 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éseau
listen_addresses 'localhost' # ou '{{INTERNAL_IP}}'
port 5432 # changer si possible
# SSL
ssl on
ssl_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'audit
log_connections on
log_disconnections on
log_failed_auth on
log_statement 'ddl' # DDL en prod (ALTER, DROP, CREATE)
# log_statement = 'all' # tout (debug uniquement)
log_duration on
log_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 minimaux
CREATE ROLE app_user LOGIN PASSWORD '{{STRONG_PASSWORD}}';
-- Connexion à la base de données
GRANT CONNECT ON DATABASE appdb TO app_user;
-- Accès au schéma
GRANT USAGE ON SCHEMA public TO app_user;
-- Privilèges sur les tables
GRANT 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 public
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT USAGE, SELECT ON SEQUENCES TO app_user;
-- Retirer le privilège CREATE du schéma public pour PUBLIC
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
-- Désactiver la connexion distante pour le superuser postgres
ALTER 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 locataire
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
CREATE POLICY orders_tenant_isolation ON orders
FOR ALL
TO app_user
USING (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écifique
ALTER 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 externe
iptables -A INPUT -p tcp --dport 3306 -s {{APP_SERVER_IP}} -j ACCEPT
iptables -A INPUT -p tcp --dport 3306 -j DROP
iptables -A INPUT -p tcp --dport 5432 -s {{APP_SERVER_IP}} -j ACCEPT
iptables -A INPUT -p tcp --dport 5432 -j DROP
# Vérifier les ports en écoute
ss -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 GPG
mysqldump --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 obligatoire
gpg --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 ProxyJump
ssh -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