Exporter les hachages de mot de passe au format PHC

Les détails actuels concernant le système de stockage des mots de passe de Discourse sont disponibles sur discourse/docs/SECURITY.md at main · discourse/discourse · GitHub. Au moment de la rédaction de ce document, nous utilisons PBKDF2-SHA256 avec 600 000 itérations.

Dans certaines situations, vous souhaiterez peut-être exporter tous les mots de passe hachés de Discourse pour les importer dans un autre système. Par exemple, vous migrez peut-être de l’authentification intégrée de Discourse vers un système SSO personnalisé. Rappelez-vous qu’il est impossible d’extraire les mots de passe originaux de la base de données ; votre système de destination doit donc être capable d’exécuter le même algorithme de hachage avec les mêmes paramètres.

Les données relatives aux mots de passe sont stockées dans la table user_passwords, qui contient les colonnes password_hash, password_salt et password_algorithm. La colonne password_algorithm stocke le préfixe complet de l’algorithme PHC (par exemple $pbkdf2-sha256$i=600000,l=32$), qui peut varier d’un utilisateur à l’autre si le nombre d’itérations a été augmenté au fil du temps.

Vous pouvez utiliser l’explorateur de données pour exporter ces informations dans un format lisible par ordinateur :

SELECT users.id, users.username, up.password_salt, up.password_hash, up.password_algorithm
FROM users
INNER JOIN user_passwords up ON users.id = up.user_id
WHERE users.id > 0

Cela exportera les données au format natif de Discourse. Le sel est encodé en hexadécimal et le hachage du mot de passe l’est également.

Certains systèmes externes prennent en charge le format de chaîne PHC, qui vise à représenter la sortie d’une fonction de hachage de mots de passe de manière inter-algorithmes. Pour pbkdf2-sha256, cette chaîne contient le type d’algorithme, le nombre d’itérations, le sel encodé en base64 et le hachage encodé en base64. Heureusement, Postgres peut gérer tout cela pour nous dans une seule requête.

Pour générer des chaînes PHC pour chaque utilisateur Discourse, vous pouvez utiliser une requête d’explorateur de données comme celle-ci :

SELECT users.id, users.username,
  concat(
    up.password_algorithm,
    replace(encode(up.password_salt::bytea, 'base64'), '=', ''),
    '$',
    replace(encode(decode(up.password_hash, 'hex'), 'base64'), '=', '')
  ) as phc
FROM users
INNER JOIN user_passwords up ON users.id = up.user_id
WHERE users.id > 0

Si vous utilisez Auth0, utilisez plutôt cette requête :

SELECT
    user_emails.email,
    users.active as email_verified,
    concat(
        up.password_algorithm,
        replace(encode(up.password_salt::bytea, 'base64'), '=', ''),
        '$',
        replace(encode(decode(up.password_hash, 'hex'), 'base64'), '=', '')
    ) as password_hash
FROM users
INNER JOIN user_passwords up ON users.id = up.user_id
INNER JOIN user_emails 
ON users.id = user_emails.user_id 
AND user_emails.primary IS TRUE
AND users.id > 0
13 « J'aime »

Une petite remarque ici : j’ai fini par ajuster (avec l’aide de l’équipe sympathique d’Auth0) l’exemple de requête pour générer des chaînes PHC valides afin d’importer les mots de passe des utilisateurs dans Auth0.

J’ai également encodé le sel en base64 en modifiant cette ligne

salt,

en

replace(encode(users.salt::bytea, 'base64'), '=', ''),

Pour en savoir plus (y compris un guide étape par étape sur la façon d’importer les utilisateurs de Discourse et leurs mots de passe dans Auth0), consultez :

1 « J'aime »

Merci @angus - c’est intéressant car nous avons eu un client qui a utilisé la requête du message d’origine pour importer avec succès des utilisateurs vers Auth0. Je me demande si quelque chose a changé dans leur processus d’importation - si ma mémoire est bonne, la possibilité d’importer des chaînes PHC vers Auth0 était très récente en novembre :thinking:

3 « J'aime »

Oui, je me posais la question et j’ai pensé la même chose.

Je n’étais pas non plus tout à fait sûr de la formulation dans la spécification PHC. Je ne sais pas si cela signifie que le sel doit être encodé en B64 ou non.

Le sel consiste en une séquence de caractères dans : [a-zA-Z0-9/+.-] (lettres minuscules, lettres majuscules, chiffres, /, +, . et -). La spécification de la fonction DOIT définir l’ensemble des valeurs de sel valides et une longueur maximale pour ce champ. Les fonctions qui fonctionnent avec des sels binaires arbitraires DOIVENT définir ce champ comme étant l’encodage B64 d’une valeur binaire dont la longueur se situe dans une plage définie ou un ensemble de plages.

2 « J'aime »

Désolé de m’éloigner du sujet, mais quelqu’un a-t-il importé des hachages de mots de passe depuis Auth0 vers Discourse ? Je pense à effectuer cette migration, donc toute aide serait appréciée. Je ne suis pas un client payant d’Auth0, alors je voulais simplement savoir si cela est réalisable avant de payer pour l’exportation des hachages de mots de passe.

Merci.

L’importation de mots de passe n’est pas prise en charge par le cœur de Discourse, bien que cela puisse être possible en utilisant une version adaptée de ce plugin tiers :

4 « J'aime »

Merci ! J’ai trouvé le dépôt sur GitHub, mais pas le sujet ici sur meta.

1 « J'aime »

Salut @angus, nous sommes en train de faire le ménage ici.

Est-il vrai que le bloc de code de l’OP devrait être :

SELECT id, username,
  concat(
    '$pbkdf2-sha256$i=64000,l=32$',
    replace(encode(users.salt::bytea, 'base64'), '=', ''),
    '$',
    replace(encode(decode(password_hash, 'hex'), 'base64'), '=', '')
  ) as phc
FROM users

et inclure quelque chose comme :

Pour plus d’informations sur l’importation des mots de passe Discourse vers Auth0, consultez Bulk User Import Custom Password Hash Issue - Auth0 Community.

Pour déplacer des données d’Auth0 vers Discourse, cela pourrait vous aider : Migrated password hashes support.

3 « J'aime »

Oui, ça a l’air bien.

Vous voudrez peut-être ajouter une mention indiquant que chaque import nécessite une attention particulière, car les données utilisateur traitées varieront selon le cas d’usage, c’est-à-dire : ne copiez-collez pas simplement ces requêtes.

De plus, l’export des mots de passe dans PHC n’est pas nécessairement réservé à Auth0, il faudrait donc peut-être simplement le présenter comme un « exemple ».

La requête complète que j’ai utilisée pour mon export était :

SELECT
    user_emails.email,
    users.active as email_verified,
    concat(
        '$pbkdf2-sha256$i=64000,l=32$',
        replace(encode(users.salt::bytea, 'base64'), '=', ''),
        '$',
        replace(encode(decode(users.password_hash, 'hex'), 'base64'), '=', '')
    ) as password_hash
FROM users
INNER JOIN user_emails 
ON users.id = user_emails.user_id 
AND user_emails.primary IS TRUE
AND users.id > 0
2 « J'aime »