Aller au contenu

Authentification scram-sha-256 et migration depuis md5

À propos

AuteurChristophe Courtois

Objet

L’authentification par mot de passe avec chiffrage du mot de passe au format SCRAM-SHA-256 est disponible depuis PostgreSQL 10. Elle est celle par défaut depuis PostgreSQL 14, mais sa mise en place est conseillée pour les versions précédentes.

Cette nouvelle méthode d’authentification est bien plus sécurisée que l’ancienne méthode avec hachage MD5. Avec cette dernière, un même mot de passe pour un même nom d’utilisateur est haché et stocké de manière identique sur tous les serveurs PostgreSQL, alors qu’avec SCRAM-SHA-256 le hachage peut être différent même pour des mots de passe identiques.

La migration des mots de passe existants en md5 à scram-sha-256 n’est pas compliquée. Elle n’est pas faite automatiquement lors d’une migration (ni par pg_upgrade ni par pg_dump) car il est impossible de déchiffrer les mots de passe pour les rechiffrer.

La cohabitation des deux systèmes est possible au cas où des clients encore incompatibles la rendraient nécessaire.

Configuration de postgresql.conf

S’il vaut encore md5, ce paramètre doit être modifié :

password_encryption = scram-sha-256

Il s’agit uniquement de la valeur par défaut, pour les mots de passé que l’on va entrer. Dans une session, on peut choisir une valeur différente. Les mots de passe existants ne sont pas affectés par ce changement.

Table pg_authid

Cette table contient les hachages des mots de passe utilisateur (champ rolpassword).

Le début du mot de passe indique le hachage, md5.. ou SCRAM-SHA-256$… (voir exemples plus bas), qui peut différer selon les utilisateurs.

Configuration du fichier pg_hba.conf

Il peut contenir des valeurs différentes selon les lignes :

# TYPE  DATABASE        USER            ADDRESS                 METHOD
host    all             dalibo          ::1/128                 md5
host    all             postgres        192.168.1.5/32          scram-sha-256

Transition

À savoir : indiquer la méthode md5 dans pg_hba.conf permet d’accepter des mots de passe chiffrés en MD5 comme en SCRAM-SHA-256. Par contre, indiquer la méthode scram-sha-256 impose que le mot de passe soit haché en SCRAM-SHA-256. Cela autorise une migration progressive des utilisateurs, certains restant dans l’ancien hachage hérité d’une version précédente de PostgreSQL, les autres ayant mis à jour leur mot de passe.

Une fois tous les mots de passe migrés, remplacer md5 par scram-sha-256 garantit que l’ancienne méthode md5 peu sécurisé ne pourra plus être utilisé.

Exemple

Soit un utilisateur créé avec un mot de passe en md5 (défaut avant la v14). Son mot de passe est stocké sous forme d’un hachage md5 :

postgres=# SHOW password_encryption ;
 password_encryption 
---------------------
 md5

postgres=# CREATE ROLE dalibo LOGIN PASSWORD 'aiWojo3roozahyah1uop' ;
CREATE ROLE

postgres=# SELECT * FROM pg_authid WHERE rolname = 'dalibo' \gx
-[ RECORD 1 ]--+------------------------------------
oid            | 4209435
rolname        | dalibo
...
rolpassword    | md58de13651207b80138624353c9e6eda71

Sa connexion est autorisée par cette ligne dans le pg_hba.conf :

host    all             dalibo          ::1/128                 md5
$ psql -h localhost -U dalibo -d postgres 
Mot de passe pour l'utilisateur dalibo : 
psql (13.2 (Ubuntu 13.2-1.pgdg20.04+1))
Connexion SSL (protocole : TLSv1.3, chiffrement : TLS_AES_256_GCM_SHA384, bits : 256, compression : désactivé)
Saisissez « help » pour l'aide.

postgres=> 

Pour mettre à jour ce mot de passe en scram-sha-256, il faut le re-rentrer après modification de password_encryption (globalement ou dans la session) :

postgres=# SET password_encryption TO "scram-sha-256" ;
SET

postgres=# \password dalibo
Saisissez le nouveau mot de passe : 
Saisissez-le à nouveau : 

postgres=# SELECT * FROM pg_authid WHERE rolname = 'dalibo' \gx
-[ RECORD 1 ]--+--------------------------------------------------------------------------------------------------------------------------------------
oid            | 4209435
rolname        | dalibo
...
rolpassword    | SCRAM-SHA-256$4096:4K6BIkV5+90l1aKfVQA17g==$FVGK4gxpLldtko08jGL7mZy5W3QacsUoxRknoiPe4UE=:a90+eT1Lh5bj9CHndrmHyblX0RxI+NdD86UaNSbrigs=

La connexion se fait alors tout aussi bien, sans modification de pg_hba.conf.

Pour sécuriser la configuration et interdire le hachage md5, il faut modifier pg_hba.conf, et recharger la configuration :

host    all             dalibo          ::1/128                 scram-sha-256

Mais si on devait décider pour une raison ou une autre de revenir en hachage md5, la connexion ne serait plus possible :

postgres=# SET password_encryption TO md5;
SET

postgres=# \password dalibo
Saisissez le nouveau mot de passe : 
Saisissez-le à nouveau : 
$ psql  -h localhost -U dalibo -d postgres 
Mot de passe pour l'utilisateur dalibo : 
psql: erreur : FATAL:  password authentication failed for user "dalibo"
FATAL:  password authentication failed for user "dalibo"

Les traces contiennent alors cette erreur :

2021-05-07 17:20:20 CEST [4078090]: [3-1] user=dalibo,db=postgres,app=[unknown],client=::1
  DETAIL:  User "dalibo" does not have a valid SCRAM secret.
  Connection matched pg_hba.conf line 99: "host    all             dalibo          ::1/128                 scram-sha-256"

Générer des mots de passes hachés

Dans un script, il n’y a personne pour fournir le mot de passe à createuser ou CREATE ROLE. Ce qui suit permet de générer des hashs depuis le mot de passe, éventuellement sur une autre machine, pour qu’il ne passe jamais en clair ni ne risque de s’afficher en clair dans les traces ni dans des vues système de PostgreSQL.

md5

Le chiffrement md5 (celui par défaut, mais le plus faible) consiste à calculer la somme md5 du mot de passe concaténé au nom du rôle, puis « md5 » est ajouté devant. Ainsi deux utilisateurs de même mot de passe n’auront pas le même mot de passe chiffré. Cela nous donne en shell, avec un utilisateur u1 et un mot de passe « supersecret »:

$ echo -n "supersecretu1" | md5sum                                              
fb75f17111cea61e62b54ab950dd1268  -                                             
$ psql postgres                                                                 
postgres=# ALTER ROLE u1 PASSWORD 'md5fb75f17111cea61e62b54ab950dd1268';        

scram-sha-256

Générer soi-même des mots de passe chiffrés en SCRAM-SHA-256 en-dehors de psql est plus compliqué qu’avec MD5, et les outils comme \password s’appuient souvent sur les fonctions fournies par la bibliothèque libpq. Cette dernière sert aussi de base à la bibliothèque python psycopg3 et sa fonction PGconn.encrypt_password()

Indépendamment de la libpq, il existe aussi un script python de Jonathan Katz (version 3.6 minimum) que nous allons utiliser ici :

# Récupération de la librairie:
wget https://gist.githubusercontent.com/jkatz/e0a1f52f66fa03b732945f6eb94d9c21/raw/c0cf06c35da300866c36ad14309a88eb183aefc3/encrypt_password.py

On l’utilise dans un script de ce genre :

#!/usr/bin/python3
from encrypt_password import EncryptPassword
import sys
login = sys.argv[1]
mdp = sys.argv[2]
pw = EncryptPassword(
user=login,
password=mdp,
algorithm="scram-sha-256",
)
print( pw.encrypt().decode("utf-8"))

appelé en fournissant utilisateur et mot de passe :

python3 scram.py robin test

Résultat :

SCRAM-SHA-256$4096:aBiN7rfgCWhPFsLI3yPOUA==$cCgRNSDprnrW7+8b6rFMB0RqMksF3ObbaSaus72lp2E=:Ph4fvVNcuBbFh/Da+jCMOrumU6a3BMIS4tRCwgAsRBU=

Ce hachage s’utilise dans CREATE ROLE … PASSWORD ou ALTER ROLE ainsi :

ALTER ROLE robin  PASSWORD 'SCRAM-SHA-256$4096:aBiN7rfgCWhPFsLI3yPOUA==$cCgRNSDprnrW7+8b6rFMB0RqMksF3ObbaSaus72lp2E=:Ph4fvVNcuBbFh/Da+jCMOrumU6a3BMIS4tRCwgAsRBU='