You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何要对密码重置Token进行哈希处理?Express密码重置功能开发疑问

为什么存储密码重置Token前需要哈希处理

你当前的功能逻辑是完全可运行的,对Token做哈希存储属于低成本高收益的纵深安全防护措施,核心目的是规避数据库泄露带来的次生风险,具体原因如下:

  • 规避数据库拖库风险:如果你的数据库被攻击者非法获取,明文存储的重置Token可以被直接用来在有效期内重置任意用户的密码,攻击者完全不需要获取用户邮箱的访问权限,就可以接管大量用户账号。而哈希后的Token是单向不可逆的,攻击者就算拿到哈希值也无法反推出可以用来提交校验的原始Token,完全阻断了这个攻击路径。
  • 几乎无额外开发成本:哈希逻辑只需要加2行代码即可实现,完全不影响原有业务流程:生成原始明文Token后先发给用户邮箱,再把原始Token做哈希后存入数据库即可,校验时把用户提交的Token做同样的哈希运算,和数据库存储的哈希值比对即可完成校验,逻辑和密码的哈希存储/校验完全一致。

修改后的参考代码如下(同时修复了原代码中邮件发送失败后未保存用户字段修改的小问题):

const forgotPassword = async (req, res) => {
  try {
    const user = await User.findOne({ email: req.body.email });
    if (!user) {
      throw Error("incorrect email");
    }
    // 生成原始明文Token,用于发送给用户
    const resetPasswordTokenPlain = crypto.randomBytes(20).toString("hex");
    // 哈希后存入数据库,不存储明文
    user.resetPasswordToken = crypto.createHash('sha256').update(resetPasswordTokenPlain).digest('hex');
    user.resetPasswordTokenExpire = Date.now() + 10 * (60 * 1000);
    await user.save();
    const message = `
      <h1>You have requested a password reset</h1>
      <p>Here is your token : </p>
      <p>${resetPasswordTokenPlain}</p>
    `;
    try {
      await sendEmail({
        to: user.email,
        subject: "Password reset request",
        text: message,
      });

      res.status(200).json({ message: "Email sent" });
    } catch (err) {
      user.resetPasswordToken = undefined;
      user.resetPasswordTokenExpire = undefined;
      await user.save();
      res.status(500).json({ message: "Email could not be sent" });
    }
  } catch (error) {
    console.log(error);
    const errorDetails = handleErrors(error);
    res.status(400).json(errorDetails);
  }
};

内容的提问来源于stack exchange,提问作者anon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 22:54:03