在已有大量用户注册后添加SSO -- 如何迁移他们?

大家好。

将所有现有的 Discourse 用户导入 intercoin.app 的正确方法是什么?是否有某种 REST 端点可以返回所有用户及其哈希密码和盐值等信息?哈希算法在 GitHub 上的链接是什么?如果我方无法使用自己的算法,我将不得不编写代码,使用相同的哈希算法和输入的密码及盐值,以便让那些用户登录。我认为当 Discourse 用户稍后启用 SSO(就像我们一样)时,第二点会变得相关,因此解决它也将有助于其他 Discourse 用户。

2 个赞

很有趣的方法。

user.rb

  def confirm_password?(password)
    return false unless password_hash && salt
    self.password_hash == hash_password(password, salt)
  end

  def hash_password(password, salt)
    raise StandardError.new("password is too long") if password.size > User.max_password_length
    Pbkdf2.hash_password(password, salt, Rails.configuration.pbkdf2_iterations, Rails.configuration.pbkdf2_algorithm)
  end

然后 Pbkdf2 代码在这里:discourse/lib/pbkdf2.rb at 201228162c277b9833bb2988388553fdbfb39521 · discourse/discourse · GitHub

2 个赞

太棒了!那么,我应该调用哪个 HTTP 端点来获取所有用户信息,包括密码哈希和盐值?

我想象中,不会有一个公开的端点(那样不是更容易被黑客攻击吗?)那么我该怎么做?连接到 MySQL 数据库?编写一个 Discourse 插件?

1 个赞

基本上就是你的选择,是的。

1 个赞

数据库模式有文档记录吗?

如何连接到 Docker 中的 PostgreSQL 数据库?如果这是个愚蠢的问题,我很抱歉。

1 个赞

我刚和我们的团队谈过,他们也同意,我愿意出钱请人开发一个小的 Discourse 插件,该插件可以通过一个端点根据用户的电子邮件地址公开一些用户的 JSON 信息。

我发现 https://github.com/discourse/discourse/blob/main/app/models/user.rb#L1855 有“password_hash”盐和用户名、用户昵称。但它没有电子邮件。为此,我看到了 user_email discourse/app/models/user_email.rb at main · discourse/discourse · GitHub

因此,给定一个电子邮件,该插件将通过邮件在 user_email 表中进行搜索,然后找到 user_id 并获取用户行,并发送所有“安全”字段,包括盐。

为了增加安全性,可以通过 HMAC 使用共享密钥对请求进行签名,该密钥可以提供给插件。

有人想做这个吗?给我发消息或在此回复,并告诉我如何联系您。希望它很简单(几个 SELECT 和一个 HMAC 检查,如果设置了密钥)。我们将读取 JSON。

1 个赞

我将直接使用现有的 admin/users/list/active.json 并通过哈希密码来扩展响应。

另外,请坚持使用现有的 API 身份验证机制,不要另起炉灶。

1 个赞

所以你的意思是有一个一次性的东西,可以导入所有加入的用户以及他们所有的盐和密码?

好的,但这仍然需要是一个插件,不是吗?所以如果有人能用 Discourse 创建一个插件那就太好了。

1 个赞

我可能会使用数据浏览器插件来导出您想要的信息。这比编写一个新插件要容易得多。

1 个赞

我如何找到这个配置值?它有什么默认值,是在代码的某个地方吗?@RGJ

root@server:~# cd /var/discourse/
root@server:/var/discourse# ./launcher enter app
检测到 x86_64 架构。
root@server:/var/www/discourse# rails c
[1] pry(main)> Rails.configuration.pbkdf2_iterations
=> 64000
[2] pry(main)>

谢谢!好的 @RGJ,有几个快速问题:

xorcist 库只是一个更快的字符串 xor,对吧?如果一个字符因为 ‘a’ 与 ‘a’ 进行 xor 而变成 0,会发生什么?字符串不是以 null 结尾的吗?

我的目标是将其移植到 PHP,所以您能提供的任何帮助(例如提供有关如何在 PHP 中复制它的信息)都将非常有帮助。

另外,这行代码 ret.bytes.map { |b| ("0" + b.to_s(16))[-2..-1] }.join("") 是在做什么?

$u = hash_hmac('sha256', $password, $salt . pack('N', 1));
$ret = $u = hash_hmac('sha256', $password, $u);
for ($i=2; $i<=$iterations; ++$i) {
  $u = hash_hmac('sha256', $password, $u);
  $ret = ($ret ^ $u);
}
// todo: 弄清楚 RUBY 在这最后一行上做什么

这接近吗?您能帮我修改一下这段 PHP 代码吗?

这是一个内置函数

$hash = hash_pbkdf2('sha256', 'YourPassword', 'YourSalt', 64000, 64, false);

通常,哈希算法在二进制数据上运行,并且结果在输出时经过十六进制或 Base64 编码。所以这不是问题。

1 个赞

非常感谢你,Richard!你为我节省了大量在用户空间 PHP 中实现它的时间!

是的!我成功创建了一个脚本,可以遍历所有 Discourse 用户,并将他们及其密码哈希导入到我们的平台。

很快,我们就能让任何拥有 Discourse 论坛的人在“讨论”标签中添加活动、视频会议、媒体等功能,并使用 Discourse。您可以在 https://intercoin.app 上看到成果。

基本上,这会将任何 Discourse 安装变成一个类似 Facebook 的现代社交网络。我们为此功能投入了多年的努力,现在我们希望将其与 Discourse 和 WordPress 紧密集成。这样,人们就可以结合使用 WordPress、Discourse 和 Qbix,并自行托管他们的整个社区。

但我还有两个遗留问题。

  1. 在 Qbix 中,我们至少使用 sha1(password + userId) 在客户端对密码进行哈希处理,然后再将其发送到服务器。即使是 HTTPS。我们这样做是为了让服务器或任何中间人(MITM)永远无法获得密码,以便在多个站点上重复使用。但是,Discourse 只会将密码发送到服务器。因此,我们不得不关闭客户端的这种哈希处理。是否可以在客户端进行一些 hash_pbkdf2 迭代,其余部分在服务器端进行?我尝试过,但似乎不匹配:
php > $password = 'abc';
php > $salt = 'def';
php > $a = hash_pbkdf2('sha256', $password, $salt, 64000, 64, false);
php > $b = hash_pbkdf2('sha256', $password, $salt, 1, 64, false);
php > $c = hash_pbkdf2('sha256', $password, $b, 63999, 64, false);
php > echo $a;
9d7a21ae4113bea06d81e0c486f45ab778bb739f19f7a6a305d8401918a9d8a1
php > echo $c;
f42af6861ebcf8560b027276e0d02ad46502636045486057d81be7c4c4aa630e
  1. 是否可以直接使用 Discourse 作为 SSO 提供商,而不是使用我们的网站作为 SSO 提供商?这样,Discourse 论坛的托管者就更有可能用 Qbix 功能来扩展它,因为登录将保持完全相同,并且在 Discourse 端。Facebook、Google 以及其他任何服务。是否有关于 Discourse Connect 作为 SSO 提供商向我们的消费者网站返回哪些信息的文档?它是否至少包含我们可以下载的照片、名字、姓氏和用户名等信息?

老实说,我认为 Discourse 通过 HTTPS 提交密码并不是你目前最大的安全挑战。

当然。我认为你可以在标准的 user serializer 中获得大部分内容。
但如果这还不够,你总是可以使用 API 从 Discourse 获取更多信息。

2 个赞

“老实说,我不认为 Discourse 通过 HTTPS 提交密码是您目前最大的安全挑战。”

真可爱。我看到了你的 sha1,然后我降低了你的 md5 :slight_smile:

我明白为什么 pbkdf2 不能真正地将其拆分……问题在于第一行:

U1 = PRF(Password, Salt + INT_32_BE(i))
U2 = PRF(Password, U1)
⋮
Uc = PRF(Password, Uc−1)

有什么办法可以拆分它吗?我想我可以用纯用户区的 php 库:pbkdf2/src/PBKDF2.php at master · Spomky-Labs/pbkdf2 · GitHub

我建议 Discourse 在通过网络发送密码之前,使用盐(userId 也可以)对内容进行哈希处理。为什么不呢?它不必与您现在数据库中存储的内容不兼容。只需在 Javascript 中执行前 100 次迭代,然后从 64000 中减去 10。您已经有了它的自定义实现(从 rails 复制的),所以您只需发送一个 isHashed 变量,如果它是 true,那么只执行“最后”的 64K-10 步。

用户 ID 在登录前是未知的,所以那行不通……

10 次迭代不安全,而 63990 次迭代比 64000 次迭代更不安全。所以,尽管只是微小的差别,但你似乎是用两种不太安全的方法和大量的额外复杂性来替换一种安全的方法。

实际的收益是什么?

1 个赞