🔝 Retour au Sommaire
Le hardening (durcissement en français) est l'ensemble des pratiques visant à renforcer la sécurité d'un système en réduisant sa surface d'attaque. Pour PostgreSQL en production, cela signifie configurer et maintenir votre base de données de manière à minimiser les risques de compromission, de fuite de données ou d'accès non autorisé.
Cette section vous guidera à travers les mesures essentielles de sécurisation d'une instance PostgreSQL, que vous soyez développeur ou DevOps débutant dans l'administration de bases de données.
Principe fondamental : La sécurité repose sur une approche en défense en profondeur (defense in depth) - plusieurs couches de protection pour qu'une défaillance unique ne compromette pas l'ensemble du système.
PostgreSQL offre plusieurs niveaux de protection :
- Réseau : Qui peut accéder au serveur ?
- Authentification : Qui êtes-vous ?
- Autorisation : Qu'avez-vous le droit de faire ?
- Chiffrement : Les données sont-elles protégées en transit et au repos ?
- Audit : Pouvons-nous tracer les actions ?
- Maintenance : Le système est-il à jour et surveillé ?
Chaque couche doit être configurée correctement pour garantir une sécurité optimale.
Par défaut, PostgreSQL écoute sur toutes les interfaces réseau. En production, vous devez restreindre cela.
Fichier : postgresql.conf
# ❌ Mauvaise pratique (par défaut dans certaines configs)
listen_addresses = '*'
# ✅ Bonne pratique : écouter uniquement sur localhost
listen_addresses = 'localhost'
# ✅ Ou spécifier les IPs autorisées
listen_addresses = '10.0.1.5,127.0.0.1'
Explication :
'*': Accepte les connexions depuis n'importe quelle interface réseau (dangereux)'localhost': Limite aux connexions locales uniquement- IPs spécifiques : Permet un contrôle granulaire
Ne vous reposez pas uniquement sur PostgreSQL - utilisez un firewall système (iptables, firewalld, nftables, ou les Security Groups en cloud).
Exemple avec firewalld (RHEL/CentOS) :
# Autoriser PostgreSQL uniquement depuis un réseau privé
sudo firewall-cmd --permanent --add-rich-rule='
rule family="ipv4"
source address="10.0.1.0/24"
port protocol="tcp" port="5432"
accept'
sudo firewall-cmd --reloadPrincipe : Bloquez le port PostgreSQL (5432 par défaut) pour tout le monde, sauf les IPs/réseaux autorisés.
Bien que la sécurité par l'obscurité ne soit pas une défense robuste, changer le port par défaut peut réduire le bruit des scans automatisés.
# postgresql.conf
port = 5433 # Au lieu de 5432
⚠️ Note : Ce n'est pas une mesure de sécurité forte, mais un complément. Ne vous y fiez jamais comme protection principale.
Le fichier pg_hba.conf (Host-Based Authentication) contrôle qui peut se connecter, depuis où, et comment.
Structure :
# TYPE DATABASE USER ADDRESS METHOD
La méthode trust autorise les connexions sans mot de passe. Elle ne doit JAMAIS être utilisée en production.
# ❌ DANGEREUX - Jamais en production !
local all all trust
# ✅ Bon - Exiger une authentification
local all all scram-sha-256
PostgreSQL 18 et versions récentes : MD5 est obsolète et déprécié. Utilisez SCRAM-SHA-256.
Configuration :
# postgresql.conf
password_encryption = scram-sha-256
pg_hba.conf :
# Connexions locales
local all all scram-sha-256
# Connexions depuis le réseau interne
host all all 10.0.1.0/24 scram-sha-256
# Connexions SSL obligatoires depuis l'extérieur
hostssl all all 0.0.0.0/0 scram-sha-256
Migration depuis MD5 :
-- Régénérer les mots de passe pour utiliser SCRAM
ALTER USER mon_user WITH PASSWORD 'nouveau_mot_de_passe';Pour les environnements modernes, PostgreSQL 18 introduit le support d'OAuth 2.0.
# pg_hba.conf
host all all 0.0.0.0/0 oauth issuer="https://auth.example.com"
Avantages :
- Centralisation de l'authentification (IdP)
- Support de l'authentification multi-facteurs (MFA)
- Gestion simplifiée des identités
Pour les connexions automatisées (applications, réplication), utilisez des certificats SSL/TLS.
# pg_hba.conf
hostssl all app_user 0.0.0.0/0 cert clientcert=verify-full
Configuration :
- Générer un certificat client
- Configurer PostgreSQL pour vérifier le certificat
- L'application présente son certificat lors de la connexion
Le rôle postgres (superutilisateur) a tous les droits. Ne l'utilisez jamais pour les applications.
Mauvaise pratique :
-- ❌ Application connectée en superutilisateur
GRANT ALL ON DATABASE myapp TO postgres;Bonne pratique :
-- ✅ Créer un utilisateur dédié avec droits minimaux
CREATE ROLE app_user WITH LOGIN PASSWORD 'secure_password';
GRANT CONNECT ON DATABASE myapp TO app_user;
GRANT USAGE ON SCHEMA public TO app_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_user; Créez des rôles pour chaque fonction :
-- Rôle lecture seule (reporting, analytics)
CREATE ROLE readonly;
GRANT CONNECT ON DATABASE myapp TO readonly;
GRANT USAGE ON SCHEMA public TO readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT ON TABLES TO readonly;
-- Utilisateur de reporting
CREATE ROLE report_user LOGIN PASSWORD 'secure_password' IN ROLE readonly;
-- Rôle application (lecture/écriture limitée)
CREATE ROLE app_rw;
GRANT CONNECT ON DATABASE myapp TO app_rw;
GRANT USAGE ON SCHEMA public TO app_rw;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_rw;
-- Utilisateur application
CREATE ROLE app_user LOGIN PASSWORD 'secure_password' IN ROLE app_rw;PostgreSQL donne des droits par défaut sur le schéma public. Révoquez-les :
-- Révoquer les privilèges publics
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
REVOKE ALL ON DATABASE myapp FROM PUBLIC; Pour aller plus loin, implémentez des politiques de sécurité au niveau des lignes.
-- Activer RLS sur une table
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
-- Politique : chaque utilisateur ne voit que ses propres données
CREATE POLICY user_isolation ON users
FOR ALL
TO app_user
USING (user_id = current_user::integer);Obligatoire en production : Toutes les connexions doivent être chiffrées.
Configuration SSL :
- Générer les certificats :
# Certificat auto-signé (développement)
openssl req -new -x509 -days 365 -nodes -text \
-out server.crt -keyout server.key \
-subj "/CN=pgserver.example.com"
chmod 600 server.key
chown postgres:postgres server.key server.crt - Configurer PostgreSQL :
# postgresql.conf
ssl = on
ssl_cert_file = '/path/to/server.crt'
ssl_key_file = '/path/to/server.key'
ssl_min_protocol_version = 'TLSv1.3' # PostgreSQL 18+
- Forcer SSL dans pg_hba.conf :
# Seules les connexions SSL sont acceptées
hostssl all all 0.0.0.0/0 scram-sha-256
Nouveauté PostgreSQL 18 : Configuration TLS 1.3 avancée
# postgresql.conf
ssl_tls13_ciphers = 'TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256'
Pour les environnements gouvernementaux ou réglementés (santé, finance) :
# postgresql.conf
ssl_fips_mode = on
FIPS (Federal Information Processing Standard) garantit l'utilisation d'algorithmes cryptographiques certifiés.
PostgreSQL ne chiffre pas les données sur disque par défaut. Options :
-
Chiffrement du système de fichiers :
- LUKS (Linux)
- BitLocker (Windows)
- dm-crypt
-
Transparent Data Encryption (TDE) :
- Solutions commerciales (EnterpriseDB, Percona)
- Extension pgcrypto pour chiffrement au niveau colonne (limité)
-
Chiffrement Cloud natif :
- AWS RDS : Encryption at rest
- Azure : Transparent Data Encryption
- GCP : Automatic encryption
Exemple pgcrypto (chiffrement colonnes sensibles) :
CREATE EXTENSION pgcrypto;
-- Chiffrer une donnée
INSERT INTO users (email, ssn_encrypted)
VALUES ('user@example.com', pgp_sym_encrypt('123-45-6789', 'secret_key'));
-- Déchiffrer
SELECT email, pgp_sym_decrypt(ssn_encrypted, 'secret_key') AS ssn
FROM users;
⚠️ Limitation : pgcrypto chiffre au niveau application, pas transparent, et n'est pas adapté pour tout le dataset.
Activez un logging détaillé pour tracer les activités suspectes.
# postgresql.conf
# Activer le logging
logging_collector = on
log_directory = 'log'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_rotation_age = 1d
log_rotation_size = 100MB
# Que logger ?
log_connections = on
log_disconnections = on
log_duration = off # Attention, verbeux
log_statement = 'ddl' # DDL uniquement (CREATE, ALTER, DROP)
# Log des erreurs et warnings
log_min_messages = warning
log_min_error_statement = error
# Durée des requêtes lentes
log_min_duration_statement = 1000 # Log si > 1 seconde
# Format du log (préfixe enrichi)
log_line_prefix = '%t [%p]: user=%u,db=%d,app=%a,client=%h '
Explication des paramètres :
log_connections/disconnections: Tracer qui se connectelog_statement = 'ddl': Auditer les modifications de schémalog_min_duration_statement: Identifier les requêtes lenteslog_line_prefix: Enrichir les logs avec contexte (user, db, IP)
Pour un audit plus fin (conformité SOX, PCI-DSS, GDPR) :
CREATE EXTENSION pgaudit;Configuration :
# postgresql.conf
pgaudit.log = 'write, ddl' # Auditer les écritures et DDL
pgaudit.log_catalog = off # Exclure les tables système
pgaudit.log_parameter = on # Logger les paramètres des requêtes
Types d'audit :
READ: SELECTWRITE: INSERT, UPDATE, DELETE, TRUNCATEDDL: CREATE, ALTER, DROPROLE: GRANT, REVOKEFUNCTION: Exécution de fonctions
Envoyez les logs vers un système centralisé (SIEM) :
-
Syslog : Configuration dans
postgresql.conflog_destination = 'syslog' syslog_facility = 'LOCAL0' syslog_ident = 'postgres' -
Solutions modernes :
- ELK Stack (Elasticsearch, Logstash, Kibana)
- Splunk
- Datadog
- AWS CloudWatch / Azure Monitor
Nouveauté PostgreSQL 18 : Les checksums de données sont activés par défaut lors de l'initialisation (initdb).
Pourquoi c'est important :
- Détecte la corruption de données sur disque
- Alerte en cas de problème matériel
- Garantit l'intégrité physique
Vérifier l'état :
SHOW data_checksums;
-- Résultat attendu : onSi désactivé (anciennes versions ou --no-data-checksums utilisé) :
- Pour activer : Nécessite un
pg_checksumsen offline (cluster arrêté) - Ou recréer le cluster avec
initdb --data-checksums
⚠️ Coût : Les checksums ont un léger impact performance (1-5%), mais c'est négligeable face au gain en détection d'erreurs.
Bien que ce soit une responsabilité applicative, configurez PostgreSQL pour limiter les dégâts.
Côté PostgreSQL :
- Limiter les permissions (voir section 3)
- Désactiver les fonctions dangereuses pour les utilisateurs non admin
Côté application (rappel) :
- Toujours utiliser des requêtes préparées (Prepared Statements)
- Jamais de concaténation de chaînes pour construire du SQL
Exemple sécurisé (Python/psycopg3) :
# ✅ Bon - Requête préparée
cursor.execute(
"SELECT * FROM users WHERE email = %s",
(user_input,)
)
# ❌ Dangereux - Vulnérable à l'injection
cursor.execute(
f"SELECT * FROM users WHERE email = '{user_input}'"
)Empêchez un utilisateur malveillant (ou buggé) de saturer le serveur.
-- Limiter les connexions par rôle
ALTER ROLE app_user CONNECTION LIMIT 10;
-- Timeout des requêtes (au niveau base ou rôle)
ALTER DATABASE myapp SET statement_timeout = '30s';
ALTER ROLE app_user SET statement_timeout = '30s';
-- Limiter la mémoire de travail
ALTER ROLE app_user SET work_mem = '64MB';Certaines fonctions permettent l'exécution de code système.
-- Révoquer l'accès aux fonctions système sensibles
REVOKE EXECUTE ON FUNCTION pg_read_file(text) FROM PUBLIC;
REVOKE EXECUTE ON FUNCTION pg_ls_dir(text) FROM PUBLIC; PostgreSQL publie des mises à jour de sécurité régulièrement.
Bonnes pratiques :
- Surveiller les annonces de sécurité : https://www.postgresql.org/support/security/
- Appliquer les patches mineurs rapidement (ex: 18.0 → 18.1)
- Tester d'abord en environnement de staging
Exemple de stratégie :
- Critique (CVE high) : Appliquer sous 7 jours
- Important (CVE medium) : Appliquer sous 30 jours
- Mineures : Appliquer au prochain cycle de maintenance
Audit régulier (mensuel ou trimestriel) :
-- Lister tous les rôles et leurs privilèges
\du
-- Lister les superutilisateurs
SELECT rolname FROM pg_roles WHERE rolsuper = true;
-- Vérifier les rôles sans mot de passe
SELECT rolname FROM pg_authid WHERE rolpassword IS NULL AND rolcanlogin = true;
-- Permissions sur les tables sensibles
SELECT grantee, privilege_type
FROM information_schema.table_privileges
WHERE table_name = 'users'; Actions :
- Désactiver les comptes inutilisés
- Supprimer les permissions obsolètes
- Forcer le renouvellement des mots de passe
Imposez une politique de rotation :
-- Expiration du mot de passe
ALTER ROLE app_user VALID UNTIL '2026-03-01';
-- Forcer le changement au prochain login (via application)
ALTER ROLE app_user PASSWORD 'new_password' VALID UNTIL '2026-03-01';Note : PostgreSQL ne force pas automatiquement le changement de mot de passe. C'est à gérer au niveau applicatif ou via un système IAM.
Utilisez des outils de scan de vulnérabilités :
- Lynis : Audit de sécurité système
- OpenVAS : Scanner de vulnérabilités réseau
- CloudWatch / Defender for SQL (Cloud) : Détection d'anomalies
Voici une checklist récapitulative à valider avant la mise en production :
-
listen_addresseslimité (pas'*') - Firewall configuré (seules IPs autorisées)
- Port modifié si pertinent
- Accès via VPN ou bastion host pour administration
- Méthode
trustdésactivée dans pg_hba.conf - SCRAM-SHA-256 activé (pas MD5)
- SSL/TLS obligatoire pour connexions externes
- Certificats SSL valides et à jour
- TLS 1.3 configuré (PostgreSQL 18)
- OAuth 2.0 configuré si applicable
- Pas de connexions applicatives en superutilisateur
- Rôles dédiés par fonction (app, readonly, admin)
- Principe du moindre privilège appliqué
- Privilèges PUBLIC révoqués
- Row-Level Security (RLS) implémenté si nécessaire
- SSL activé pour toutes les connexions
- Certificats SSL valides (pas auto-signés en prod)
- Chiffrement au repos (filesystem ou TDE)
- Mode FIPS activé si requis
- Logging activé (connections, disconnections, DDL)
- Logs centralisés (syslog, ELK, SIEM)
- pgAudit installé et configuré (si conformité)
- Rotation des logs configurée
- Alertes configurées sur logs critiques
- Data checksums activés
- Sauvegardes régulières et testées
- Réplication configurée (HA)
- Requêtes préparées utilisées (pas de concaténation)
- Timeouts configurés (statement_timeout)
- Limites de connexions par rôle
- Fonctions système désactivées pour users non-admin
- Process de mise à jour de sécurité défini
- Audit des comptes et permissions mensuel
- Rotation des mots de passe planifiée
- Monitoring de sécurité actif
- Documentation à jour (runbook sécurité)
- pgAudit : https://github.com/pgaudit/pgaudit
- Lynis : https://cisofy.com/lynis/
- pg_permissions : Outil d'audit des permissions
- CIS PostgreSQL Benchmark : Guide de hardening
- NIST Cybersecurity Framework
- OWASP Database Security Cheat Sheet
- Mailing list pgsql-security
- PostgreSQL Security Announcements : https://www.postgresql.org/support/security/
Le hardening sécurité n'est pas une étape unique, mais un processus continu :
- Configuration initiale : Appliquer les mesures de base
- Audit régulier : Vérifier les permissions, logs, mises à jour
- Adaptation : Ajuster en fonction des nouvelles menaces et besoins
Principe clé : Une sécurité efficace repose sur la défense en profondeur. Aucune mesure unique ne suffit - c'est la combinaison de toutes ces pratiques qui protège réellement votre base de données.
En suivant cette checklist et en maintenant une vigilance constante, vous réduisez considérablement la surface d'attaque de votre instance PostgreSQL et assurez la protection des données confiées à votre système.
Prochaine section : 19.6.2. Configuration optimale