越权漏洞风险及解决方案

1、越权分类

常见越权有水平越权、垂直越权和数据越权。

1.1 水平越权

用于A访问接口通过修改接口的参数操作了用户B的数据。比如查询订单列表接口,通过传递别人的userId查询别人的订单列表。这种问题出现的原因是接口直接使用用户传递的参数做租户隔离,并没有拿用户的参数与登录信息做对比,或者没有取登录信息里的用户信息做业务逻辑。解决办法:用户信息userInfo从登录信息里取。

1.2 垂直越权

用户A访问了超管接口/没有权限的接口。解决办法:做功能码鉴权,也就是做权限限制。可以为每个接口定义一个功能码,可在常量类里定义。然后在接口上添加自定义注解,把功能码添进去。然后用AOP做切面,在切面里读取登录用户的权限,查询权利里的功能码,看用户是否有当前接口权限。

1.3 数据越权

可以理解为水平越权的特例,接口使用了数据ID做参数。比如一个订单详情查询接口,入参是订单号,而订单号往往有规律,用户A在自己的一个合法的订单号基础上+1,就可能得到别人的一个存在的订单号,然后调用订单详情查询接口。

解决办法:查询接口默认添加一个用户信息做参数,用当前用户的登录信息做参数。或者先查询出来数据,在内存里对比用户信息。

2、其他安全措施

2.1 签名

与三方系统对接,无法通过登录信息做鉴权的,可以设计签名,预防接口被随意调用。解决方法:给三方系统颁发一个appId、Secret(记录在自己系统里),要求调用方传递参数时带上appId,然后传递一个签名sign,sign可以设计为 md5(业务参数+Secret)。接收方根据appId查询出来对应的Secret,然后用同样的算法计算sign,对比一致即可放行。设计签名,攻击方无法修改业务参数,因为修改了业务参数,sign便不对了,只要调用方不泄露secret,它就是安全的。接收方做好幂等,业务也不会有问题。最多被重放请求(ddos),并且重放也可以通过参数里加时间戳来解决。

2.2 数据脱敏

所有鉴权问题搞定后,数据字段透出最好做到最小化,前端不需要的字段不透出,敏感字段要脱敏。

3、代码实例

/**
     * 获取订单详情
     * @param orderNo
     * @return
     */
    @RequestMapping(value = "order/info",method = RequestMethod.POST)
    @FunctionAuth(FunctionConstants.QUERY_ORDER)
    public ResponseVo getOrder(@RequestParam("orderNo") String orderNo) {
        // 基础非空校验
        Assert.isTrue(!StringUtils.isEmpty(orderNo));

        // 登录校验,从登录信息里获取用户信息
        LogInUserInfo userInfo = LoginUtil.getUserInfo();

        OrderInfo order = OrderService.getOrder(orderNo);
        
        // 水平越权
        if (order != null && order.getUserId() != userInfo.getId()) {
            throw new BizException("非法订单查询");
        }

        // 脱敏处理
        order = SensitiveUtil.del(order);

        return new ResponseVo(order);
    }
经验分享 程序员 微信小程序 职场和发展