将 Discourse 用作账户提供商和 PBKDF2 问题

您好。这可能是一个愚蠢的问题,但我们正在将论坛从 XenForo 迁移到 Discourse。我们有一个用于授权的后端服务器,该服务器涉及连接到数据库并将凭据与 users 表进行验证。

XenForo 的 bcrypt 算法按预期工作,没有任何麻烦。然而,当我们迁移到 Discourse 时,PBKDF2 算法似乎不符合我的预期。相同的密码、相同的盐、相同的迭代次数和长度,但输出的哈希值却不同。

我尝试了 PBKDF2 算法的各种不同实现,但它们都输出了完全相同的(与 Discourse 不同的)哈希值。包括我自己的实现。
我宁愿避免使用 OAuth2 或 SSO 等机制,因为它们会增加额外的开销和工作量。

有人将 Discourse 用于此类用例吗?如果有,您是如何解决这个问题的?

您在使用 migrate password 插件吗?

不,至少据我所知没有。您是指这个吗?

1 个赞

你尝试过openssl的实现吗?那正是我们使用的(你可以在discourse/lib/pbkdf2.rb中看到)。

例如,在将用户的密码设置为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 个赞

我们主要为 Xenforo 使用了 Go 的 crypto/bcrypt 实现。来自各种 pbkdf2 算法实现的相同哈希值表明 Go 可能以某种不同的方式存储字符串或将字符串转换为字节。

我明天得试试(这里已经很晚了)。如果 OpenSSL 能给我想要的结果,那么我将不得不为 Go 寻找 OpenSSL 绑定,或者我将不得不切换到一种完全不同的语言(具有 OpenSSL 绑定)来作为后端。

您有一个简短的测试用例吗?

例如,如果您使用上面的信息,您会得到什么?

1 个赞

抱歉,由于时区问题,我现在无法告知您。这里已经很晚了,我只能明天再告诉您。

我已按您的要求操作。密码是 swordfish#98765

数据库条目:

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

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 {
	// error handling...
}

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

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

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))

上述代码的输出:

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

算了。我必须使用盐的十六进制表示形式作为参数,而不是像我在上面的帖子中所做的那样使用解码后的盐。现在哈希值相等了。

5 个赞

这曾经也是我的理论!起初我也犯了同样的错误。

1 个赞

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