Utilizzo di Discourse come provider di account e problemi PBKDF2

Ciao. Questa è probabilmente una domanda stupida, ma stiamo migrando il nostro forum da XenForo a Discourse. Abbiamo un server backend per l’autorizzazione che prevede la connessione al database e la verifica delle credenziali rispetto alla tabella degli utenti.

L’algoritmo bcrypt di XenForo ha funzionato come previsto e senza alcun problema. Quando siamo passati a Discourse, tuttavia, l’algoritmo PBKDF2 non sembra aver soddisfatto le mie aspettative. Stessa password esatta, stesso salt esatto, stesso numero di iterazioni ed esatta lunghezza, ma l’hash di output è diverso.

Ho provato varie implementazioni dell’algoritmo PBKDF2, ma tutte producono lo stesso identico hash (diverso da quello di Discourse). Inclusa la mia implementazione.
Preferirei evitare meccanismi come OAuth2 o SSO a causa dell’overhead aggiuntivo e del lavoro extra che ci impongono.

Qualcuno ha utilizzato Discourse per tali casi d’uso e, in tal caso, come ha risolto questo problema?

Stai usando il plugin migrate password?

No, almeno per quanto ne so. Ti riferisci a questo?

1 Mi Piace

Hai provato l’implementazione di openssl? È quella che usiamo (puoi vederla in discourse/lib/pbkdf2.rb).

Come esempio, dopo aver impostato la password di un utente su swordfish#:

discourse_development=# select password_hash, salt, password_algorithm from users where id=2;
-[ RECORD 1 ]------+-----------------------------------------------------------------
password_hash      | 67650523776bdc87ebcd2fc11719553c87b11e6c4da49806d9d5232460d2adc9
salt               | 712ef44dd6fe6d6f0f1b6f702bb78459
password_algorithm | $pbkdf2-sha256$i=600000,l=32$
$ openssl kdf \
  -kdfopt pass:'swordfish#' \
  -kdfopt salt:712ef44dd6fe6d6f0f1b6f702bb78459  \
  -kdfopt digest:SHA2-256 \
  -kdfopt iter:600000 \
  -keylen 32 \
  PBKDF2 \
  | tr -d : | tr '[:upper:]' '[:lower:]'
67650523776bdc87ebcd2fc11719553c87b11e6c4da49806d9d5232460d2adc9
1 Mi Piace

Abbiamo utilizzato principalmente l’implementazione crypto/bcrypt di Go per Xenforo. Gli stessi hash di varie implementazioni dell’algoritmo pbkdf2 mi suggeriscono che Go possibilmente memorizza stringhe o converte stringhe in byte in un modo alquanto diverso.

Dovrò provarlo domani (è tardi qui). Se OpenSSL mi darà il risultato desiderato, allora dovrò cercare binding OpenSSL per Go, oppure dovrò passare a una lingua completamente diversa (che ha binding OpenSSL) per il backend.

Hai un breve caso di test di esempio?

Ad esempio, se usi le informazioni sopra, cosa ottieni?

1 Mi Piace

Mi dispiace, al momento non sono in grado di dirtelo perché i fusi orari sono fastidiosi. Qui è molto tardi e potrei farlo solo il giorno successivo.

Ho fatto come hai chiesto. La password è swordfish#98765.

Voce del database:

discourse=> SELECT password_hash, salt, password_algorithm FROM users WHERE id=1;
                          password_hash                           |               salt               |      password_algorithm
------------------------------------------------------------------+----------------------------------+-------------------------------
 db3f0829e66336323e81110a1792a76000b9c60605e1fa6964797ea1b07c33c6 | 0d079078e220158011afaf497794166d | $pbkdf2-sha256$i=600000,l=32$
(1 row)

OpenSSL:

/var/discourse# openssl kdf \
> -kdfopt pass:'swordfish#98765' \
> -kdfopt salt:0d079078e220158011afaf497794166d \
> -kdfopt digest:SHA2-256 \
> -kdfopt iter:600000 \
> -keylen 32 \
> PBKDF2 \
> | tr -d : | tr '[:upper:]' '[:lower:]'
db3f0829e66336323e81110a1792a76000b9c60605e1fa6964797ea1b07c33c6

Codice Go:

var userId int
var hash string
var salt string
var active bool

row := s.Database.QueryRow(`
	SELECT u.id, u.password_hash, u.salt, u.active
	FROM users AS u
	INNER JOIN user_emails AS ue ON u.id = ue.user_id
	WHERE ue.email = $1;`,
	email,
)

if err := row.Scan(&userId, &hash, &salt, &active); err != nil {
	// gestione errore...
}

hashBytes, err := hex.DecodeString(hash)
if err != nil {
	// gestione errore...
}

saltBytes, err := hex.DecodeString(salt)
if err != nil {
	// gestione errore...
}

key := pbkdf2.Key([]byte(password), saltBytes, 600000, 32, sha256.New)

fmt.Printf("salt: %v\n", salt)
fmt.Printf("hash: %v\n", hash)
fmt.Printf("hex.EncodeToString(key): %v\n", hex.EncodeToString(key))

Output del codice sopra:

salt: 0d079078e220158011afaf497794166d
hash: db3f0829e66336323e81110a1792a76000b9c60605e1fa6964797ea1b07c33c6
hex.EncodeToString(key): b378c12d96ac62a6099fc674d334f0793e6294f7927da0badc811e794a960802

Non importa. Ho dovuto usare la rappresentazione esadecimale del sale come argomento, non il sale decodificato come stavo facendo nel post precedente. Ora gli hash sono uguali.

5 Mi Piace

Anche questa era la mia teoria! Ho fatto lo stesso errore all’inizio.

1 Mi Piace

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.