PHC 형식으로 비밀번호 해시 내보내기

Discourse의 비밀번호 저장 시스템에 대한 최신 세부 사항은 https://github.com/discourse/discourse/blob/main/docs/SECURITY.md에서 확인할 수 있습니다. 작성 시점 기준으로 우리는 600,000회 반복을 사용하는 PBKDF2-SHA256 알고리즘을 사용하고 있습니다.

일부 상황에서는 Discourse에서 해시된 모든 비밀번호를 내보내고 다른 시스템에 가져와야 할 수 있습니다. 예를 들어, Discourse의 내장 인증에서 사용자 지정 SSO 시스템으로 마이그레이션하는 경우입니다. 데이터베이스에서 원래 비밀번호를 추출하는 것은 불가능하므로, 대상 시스템은 동일한 매개변수로 동일한 해싱 알고리즘을 실행할 수 있어야 합니다.

비밀번호 데이터는 user_passwords 테이블에 저장되며, 여기에는 password_hash, password_salt, password_algorithm 열이 포함되어 있습니다. password_algorithm 열에는 전체 PHC 알고리즘 접두사(예: $pbkdf2-sha256$i=600000,l=32$)가 저장되며, 시간이 지남에 따라 반복 횟수가 증가한 경우 사용자별로 달라질 수 있습니다.

데이터 탐색기(data explorer)를 사용하여 정보를 컴퓨터가 읽을 수 있는 형식으로 내보낼 수 있습니다:

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

이렇게 하면 데이터를 Discourse의 네이티브 형식으로 내보냅니다. 솔트(salt)는 16진수(hex)로 인코딩되고, 비밀번호 해시도 16진수(hex)로 인코딩됩니다.

일부 외부 시스템은 비밀번호 해싱 함수의 출력을 알고리즘 간에 표현하기 위한 목적으로 설계된 PHC 문자열 형식을 지원합니다. pbkdf2-sha256의 경우, 이 문자열에는 알고리즘 유형, 반복 횟수, base64로 인코딩된 솔트 및 base64로 인코딩된 해시가 포함됩니다. 다행히도 Postgres는 단일 쿼리로 이 모든 것을 처리해 줄 수 있습니다.

각 Discourse 사용자를 위한 PHC 문자열을 생성하려면 다음과 같은 데이터 탐색기 쿼리를 사용할 수 있습니다:

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

Auth0을 사용하는 경우 대신 다음을 사용해야 합니다:

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개의 좋아요

Just a note here that I (with some help from the friendly Auth0 team) ended up tweaking the example query to generate valid PHC strings for importing user passwords into Auth0.

I also encoded the salt as base64 by changing this line

salt,

to

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

See further here (including a step by step on how to import Discourse users and their passwords into Auth0).

1개의 좋아요

Thanks @angus - this is interesting because we have had a customer use the query in the OP to successfully import users to Auth0. I wonder if something has changed in their import process - IIRC the ability to import PHC strings to Auth0 was very new back in November :thinking:

3개의 좋아요

Yeah, I was wondering about that, and thought the same.

I also wasn’t quite sure of the language in the PHC specification. Not sure if this means the salt must be B64 encoded or not.

The salt consists in a sequence of characters in: [a-zA-Z0-9/+.-] (lowercase letters, uppercase letters, digits, /, +, . and -). The function specification MUST define the set of valid salt values and a maximum length for this field. Functions that work over arbitrary binary salts SHOULD define that field to be the B64 encoding for a binary value whose length falls in a defined range or set of ranges.

2개의 좋아요

Sorry for getting off-topic, but has anyone imported password hashes from Auth0 to Discourse? I’m thinking of doing this migration so any help would be appreciated. I’m not a paying Auth0 customer so I just wanted to know if this is feasible before paying for the password hash export.

Thanks.

Importing passwords is not supported by Discourse core, although it might be possible using an adapted version of this third-party plugin:

4개의 좋아요

Thank you! I’ve found the repo on GitHub but not the topic here on meta.

1개의 좋아요

Hey @angus, we’re cleaning things up here.

Is it true that the OP code block should read:

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

and include something like:

For more information about importing Discourse passwords to Auth0 see Bulk User Import Custom Password Hash Issue - Auth0 Community.

To move data from Auth0 to Discourse, this might help: Migrated password hashes support.

3개의 좋아요

Yup, that looks good.

You may want to add something in about each import needing specific attention, as the user data being handled will differ depending on the use case, i.e. don’t just copy / paste these queries.

Also, exporting passwords in PHC is not necessarily only for Auth0, so perhaps that should just be refered to as an “example”.

The full query I used for my export was

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개의 좋아요