【问题标题】:Problem implementing password reset functionality (stopping users from using old passwords) - MongoDB & NodeJS实现密码重置功能的问题(阻止用户使用旧密码)-MongoDB 和 NodeJS
【发布时间】:2022-01-12 11:36:45
【问题描述】:

我正在为 Web 应用程序实现身份验证,并希望实现忘记/重置密码功能,并且用户在重置密码时不应该输入旧密码。我的具体问题是这样的:

我有 1 个看起来像这样的猫鼬用户模型:

const UserSchema = new mongoose.Schema({
  email: {
    type: String,
    required: true,
    unique: true,
  },
  password: {
    type: String,
    required: true,
    select: false,
  },
  passwordChangedAt: {
    type: Date,
    required: false,
    default: null,
  },
  role: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Role',
    required: true,
  },
  ...Base,
});

我也有一个密码重置模型:

const PasswordResetSchema = new mongoose.Schema({
  user: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User',
    required: true,
  },
  token: {
    type: String,
    required: true,
  },
  isUsed: {
    type: Boolean,
    required: false,
    default: false,
  },
  expireDate: {
    type: Date,
    required: true,
  },
  oldPassword: {
    type: String,
    required: true,
  },
  newPassword: {
    type: String,
    required: false,
    default: null,
  },
  ...Base,
});

现在重置密码时,我想在密码重置表中查找,并检查 oldPassword 或 newPassword 之前是否已经使用过,如果使用过,则向用户显示他们曾经使用过该密码的错误。我使用bcrypt 使用盐对密码进行哈希处理,这意味着相同的密码可以在数据库中具有不同的哈希值。所以问题是在查询密码重置集合时,对于之前使用的相同密码,有没有办法在比较哈希和新的明文密码时查询密码以检查密码是否被使用。

  const hashedPassword = await bcrypt.hash(password, bcrypt.genSaltSync());
  // Here the hashedPassword is different, since the salt is different

  const usedSamePassPreviously = await PasswordReset.find({
    _id: { $nin: [passwordReset._id] },
    user: user._id,
    $or: [{ oldPassword: hashedPassword }, { newPassword: hashedPassword }],
    isUsed: true,
  });

我还尝试使用 mongo 中的 $where 子句进行查询,它允许我使用纯 JavaScript 函数,但是我们无法访问 bcrypt.compare 函数。我不想在后端加载所有数据,然后查找是否使用了密码,或者对所有密码使用相同的盐,因为这是一个安全问题。我如何处理这个查询,是否可以在数据库端实现这一点。提前致谢。

【问题讨论】:

  • // 从数据库中获取旧密码列表。如果匹配,则循环并承诺比较结果 BCRYPT.compare(req.body.newpassword, results[index].oldpassword, (err, result) => { // 如果匹配,则抛出错误...}; // 如果全部成功,没有进行比较,则创建新的哈希并插入它。

标签: javascript node.js mongodb mongoose bcrypt


【解决方案1】:

您需要遍历所有禁止密码的哈希值,并在每个密码上使用 bcrypt 的 .compare() 方法。这样您就可以看到您的用户提供给您的新密码值是否应该作为重复而被拒绝。

在您的客户端代码中执行此操作。

确实,做.compare() 需要时间;这是一个设计特点。这就是您应该在数据库服务器中执行此操作的原因:数据库是共享资源,您不想在那里执行 CPU 密集型长时间运行操作;他们将占用为其他数据库客户端代码提供服务所需的 CPU。在您的 nodejs 代码中执行这些操作。如果性能确实有问题,请考虑使用工作线程。

并且,考虑信息安全含义:要在您的数据库中执行此操作,您必须将用户提供给您的秘密明文密码发送给它。你应该避免在任何不是绝对需要的地方发送秘密。数据库软件庞大而复杂,为网络爬虫提供了很大的攻击面。因此,向它发送它不需要的秘密会使这些秘密变得更容易受到攻击。即使您实际上并没有存储秘密也是如此。

【讨论】:

  • 是的,我明白,有没有一种方法可以在数据库级别而不是在后端使用 JavaScript 做到这一点。由于在后端加载所有密码重置然后进行比较,成本会很高。
  • 请看我的编辑。感谢您阐明我对您问题的理解。
  • 我明白了,感谢您抽出宝贵时间帮助我解决此问题。
猜你喜欢
  • 2020-07-05
  • 2017-03-06
  • 2013-09-22
  • 1970-01-01
  • 1970-01-01
  • 2018-06-04
  • 2023-03-30
  • 2021-12-22
  • 2019-06-04
相关资源
最近更新 更多