简体   繁体   English

实现密码重置功能的问题(阻止用户使用旧密码) - MongoDB & NodeJS

[英]Problem implementing password reset functionality (stopping users from using old passwords) - MongoDB & NodeJS

I am implementing auth for a web app and want to implement forgot/reset password functionality, and the user is not supposed to enter old used passwords when resetting their password.我正在为 web 应用程序实现身份验证,并希望实现忘记/重置密码功能,并且用户在重置密码时不应输入旧密码。 My specific question is this:我的具体问题是这样的:

I have 1 mongoose user model that looks like this:我有 1 个 mongoose 用户 model 看起来像这样:

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,
});

I also have one password reset model:我还有一个密码重置model:

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,
});

Now when resetting the password I want to lookup in the password reset tables, and check if oldPassword or newPassword was already used before, and if it was used show an error to the user that they already once used that password.I use bcrypt to hash the password using a salt, which means that the same password can have a different hash in the database.现在,在重置密码时,我想在密码重置表中查找,并检查之前是否已经使用过 oldPassword 或 newPassword,如果使用过,则会向用户显示他们曾经使用过该密码的错误。我使用 bcrypt 到 hash使用盐的密码,这意味着相同的密码在数据库中可以有不同的hash。 So the problem is when querying the password reset collection, for same passwords use before, is there a way to query the passwords while comparing hashes and new plaintext password to check if the password was used.所以问题是在查询密码重置集合时,对于以前使用的相同密码,有没有办法在比较哈希和新的明文密码时查询密码以检查密码是否被使用。

  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,
  });

I also tried querying using the $where clause in mongo, which allows me to use pure JavaScript function, but there we dont have access to bcrypt.compare function.我还尝试使用 mongo 中的 $where 子句进行查询,它允许我使用纯 JavaScript function,但我们无法访问 bcrypt.compare ZC1C425268E683854D1AB5074C17A。 I dont want to load all data in back-end and then find if a password was used, or use the same salt for all passwords since that is a security problem.我不想在后端加载所有数据,然后查找是否使用了密码,或者对所有密码使用相同的盐,因为这是一个安全问题。 How can i approach this query and is it possible to achieve this in the database side.我如何处理这个查询,是否可以在数据库端实现这一点。 Thanks in advance.提前致谢。

You'll need to iterate over the hashes of all your forbidden passwords and use bcrypt's .compare() method on each one.您需要遍历所有禁止密码的哈希值,并在每个密码上使用 bcrypt 的.compare()方法。 That way you can see whether the new password value presented to you by your user should be rejected as a duplicate.通过这种方式,您可以查看用户提供给您的新密码值是否应该作为重复而被拒绝。

Do this in your client code.在您的客户端代码中执行此操作。

It's true that doing .compare() takes time;确实,做.compare()需要时间。 that's a design feature.这是一个设计特点。 That's a reason you should not do it in your database server: the database is a shared resource and you don't want to do cpu-intensive long running operations there;这就是您应该在数据库服务器中执行此操作的原因:数据库是共享资源,您不想在那里执行 CPU 密集型长时间运行操作; they'll take CPU needed to serve other database client code.他们将占用为其他数据库客户端代码提供服务所需的 CPU。 Do those operations in your nodejs code.在您的 nodejs 代码中执行这些操作。 If the performance is really a problem, consider using a worker thread.如果性能确实有问题,请考虑使用工作线程。

And, consider the information security implication: To do this in your database you must send it the secret plaintext password presented to you by your user.并且,考虑信息安全含义:要在您的数据库中执行此操作,您必须将用户提供给您的秘密明文密码发送给它。 You should avoid sending secrets anywhere they're not absolutely needed.你应该避免在任何不是绝对需要的地方发送秘密。 Database software is large and complex, and presents a large attack surface to cybercreeps.数据库软件庞大而复杂,为网络爬虫提供了很大的攻击面。 So sending it secrets it doesn't need makes those secrets just a little more vulnerable.因此,向它发送它不需要的秘密会使这些秘密变得更容易受到攻击。 That's true even if you don't actually store the secrets.即使您实际上并没有存储秘密也是如此。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM