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

Node.js中防止用户越权查看修改其他商户停车数据的方法

问题根因

越权漏洞由四个逻辑错误共同导致:

  • 关联ID不匹配:Parking集合的merchantId字段关联的是Merchant集合的文档_id,而JWT中存储的req.data.id是Auth集合的用户主键,二者没有直接映射关系,原有代码中merchantId: req.data.id的条件从一开始就无法匹配正确的所属关系。
  • 查询方法误用:findByIdAndUpdate默认以主键_id作为核心匹配条件,传入包含merchantId的多条件对象时,部分Mongoose版本会直接忽略_id外的其他字段,导致权限过滤逻辑完全不生效。
  • 缺失结果校验:findByIdAndUpdate/findOneAndUpdate未匹配到符合条件的文档时会返回null,原有代码没有判断返回值,无论是否真的完成更新都直接返回成功响应,非法操作也会收到成功提示。
  • 缺失账号类型校验:接口没有判断当前登录用户的accountType,普通用户、服务商账号也能直接发起停车数据修改请求。
修复方案

按照以下逻辑调整即可实现严格权限管控:

  1. 接口入口先校验登录用户存在,且账号类型为merchant,非商户账号直接返回403错误
  2. 通过当前登录用户的Auth ID,查询绑定的Merchant文档,拿到商户主体自身的_id
  3. 改用findOneAndUpdate方法,同时传入停车记录ID、当前商户ID两个匹配条件执行更新
  4. 判断更新操作的返回值,若为null说明要么停车记录不存在,要么不属于当前商户,直接返回403权限错误
  5. 停车详情、列表查询接口复用相同的过滤逻辑,查询时始终携带当前商户ID作为过滤条件,避免越权查看
修正后的完整接口代码
exports.updateParking = async (req, res) => {
  try {
    const { parkingName, price, address, name, phoneNumber, about } = req.body;
    const { parkingImage } = req.files;

    // 校验登录用户身份与账号类型
    const currentAuthUser = await Auth.findById(req.data.id);
    if (!currentAuthUser) return res.status(404).json({ error: "用户不存在" });
    if (currentAuthUser.accountType !== "merchant") {
      return res.status(403).json({ error: "仅商户账号可操作停车数据" });
    }

    // 查询当前账号绑定的商户主体ID
    const bindMerchant = await Merchant.findOne({ merchantId: req.data.id });
    if (!bindMerchant) {
      return res.status(403).json({ error: "当前账号未绑定商户主体,无操作权限" });
    }

    // 携带商户权限条件执行更新
    const updatedParking = await Parking.findOneAndUpdate(
      {
        _id: req.params.id,
        merchantId: bindMerchant._id
      },
      {
        $set: {
          parkingName,
          price,
          address,
          name,
          phoneNumber,
          about
          // 其余待更新字段按业务需求补充
        }
      },
      { new: true } // 配置后返回更新后的完整文档,不需要可删除
    );

    // 无匹配文档直接返回权限错误
    if (!updatedParking) {
      return res.status(403).json({ error: "停车数据不存在,或你无权限操作该数据" });
    }

    return res.status(200).json({
      success: true,
      msg: "停车信息更新成功",
      data: updatedParking
    });
  } catch (error) {
    return res.status(500).json({ error: error.message });
  }
};
优化建议
  • 可以把「根据Auth用户ID查询绑定商户ID」的逻辑整合到JWT认证中间件中,认证通过后直接将商户ID挂载到req对象上,避免所有商户接口重复写查询逻辑
  • 为Parking集合的merchantId字段创建普通索引,大幅提升权限校验、关联查询的执行效率
  • 所有涉及停车数据的写操作(删除、修改状态等)都要复用上述权限校验逻辑,不要只在更新接口做校验

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 06:51:22