美文网首页
一种垂直越权的解决方案

一种垂直越权的解决方案

作者: 文景大大 | 来源:发表于2021-02-02 21:25 被阅读0次

垂直越权是一种非常常见且非常严重的权限漏洞,具体表现就是,低权限的用户可以不受控制的访问高权限用户的资源。

其实业界有现成的权限框架可以解决这个问题,比如Shiro、SpringSecurity,但是框架一般都比较重,如果我们的系统对权限校验的要求比较简单,那么就可以考虑自己来实现一套防止垂直越权的体系。

在开始如下方案的介绍前,需要拥有Spring拦截器、自定义注解、JavaConfig的相关知识。

方案一、

基于资源限定的角色来进行垂直越权控制

我们先构建一个基础的RestDemo应用,采用Spring Boot构建,然后新建一个RestController类如下:

@RestController
public class AuthTest {

    @RolePermitted(roleList = {"admin","leader"})
    @GetMapping(value = "/getMoney")
    public String getMoney(){
        return "1000";
    }

    @RolePermitted(roleList = {"admin","leader","employee"})
    @GetMapping(value = "/storeMoney/{num}")
    public String storeMoney(@PathVariable("num") String num){
        return num + " money stored";
    }

}

然后定义一个自定义注解@RolePermitted,用来表示哪些角色可以访问当前这个资源。

@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface RolePermitted {
    String[] roleList();
}

以上,我们已经完成了基本内容的开发,剩下的就是创建一个拦截器并进行配置的注册了。

@Slf4j
public class RoleBasedAuthInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HandlerMethod handlerMethod = (HandlerMethod)handler;
        Method method = handlerMethod.getMethod();

        // 对标注了RolePermitted注解的方法访问进行权限校验
        if(method.isAnnotationPresent(RolePermitted.class)){
            checkRolePermission(method);
        }

        // 只有当权限校验通过,不抛出异常的时候才能通过
        return true;
    }

    private void checkRolePermission(Method method) throws Exception {
        // 获取当前方法允许访问的角色列表
        String[] roleList = method.getAnnotation(RolePermitted.class).roleList();

        // 模拟从数据库/redis/缓存中获取用户的实际角色是employee
        String currentUserRole = "employee";

        for (String role : roleList) {
            if(role.equals(currentUserRole)){
                log.info("{}权限校验通过, 允许访问的角色列表是{},当前用户角色是{}", method.getName(), roleList ,currentUserRole);
                return;
            }
        }

        log.warn("{}权限校验不通过, 允许访问的角色列表是{},当前用户角色是{}", method.getName(), roleList ,currentUserRole);
        throw new Exception("权限校验不通过");
    }
}
@Configuration
@EnableWebMvc
public class AuthConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new RoleBasedAuthInterceptor());
    }
}

此时,我们启动应用后,对目前仅有的全部两个URL的访问就都会走我们的RoleBasedAuthInterceptor拦截器进行角色的垂直越权校验,因为此时我们模拟的登录用户角色是employee,所以当访问/storeMoney/{num}时没有问题,但是访问/getMoney时就抛出了异常。

这种方案的优点是,实现起来非常简单,便于后续的维护,我们只需要对新增的URL添加对应的允许访问角色即可。但是缺点也非常的明显:

  • 对于角色很多的系统,我们不得不在注解上加上很多允许访问的角色,会显得很冗长;
  • 如果后续需要新增一个角色,那么就需要找到所有该角色允许访问的URL,挨个增加角色信息,费时费力,还容易遗漏;
  • 万一在系统运行期间需要灵活调配不同角色允许访问的URL,几乎是不可能的,需要重新修改代码部署;

方案二、

基于资源和角色的配置关系来进行垂直越权控制

@RestController
public class AuthTest {

    @ResourceCode(resourceCode = "getMoney")
    @GetMapping(value = "/getMoney")
    public String getMoney() {
        return "1000";
    }

    @ResourceCode(resourceCode = "storeMoney")
    @GetMapping(value = "/storeMoney/{num}")
    public String storeMoney(@PathVariable("num") String num) {
        return num + " money stored";
    }

}
@Slf4j
public class ResourceCodeBasedAuthInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HandlerMethod handlerMethod = (HandlerMethod)handler;
        Method method = handlerMethod.getMethod();

        // 对标注了ResourceCode注解的方法访问进行权限校验
        if(method.isAnnotationPresent(ResourceCode.class)){
            checkRolePermission(method);
        }

        // 只有当权限校验通过,不抛出异常的时候才能通过
        return true;
    }

    private void checkRolePermission(Method method) throws Exception {
        // 获取当前方法的资源Code
        String resourceCode = method.getAnnotation(ResourceCode.class).resourceCode();

        // 模拟从数据库/redis/缓存中获取用户的可访问资源code列表
        List<String> allowedResourceCodeList = new ArrayList<>();
        allowedResourceCodeList.add("storeMoney");
        allowedResourceCodeList.add("transferMoney");

        if(allowedResourceCodeList.contains(resourceCode)){
            log.info("{}权限校验通过, 当前用户的资源列表为{},当前资源的code为{}", method.getName(), allowedResourceCodeList ,resourceCode);
            return;
        }

        log.warn("{}权限校验不通过, 当前用户的资源列表为{},当前资源的code为{}", method.getName(), allowedResourceCodeList ,resourceCode);
        throw new Exception("权限校验不通过");
    }
}
@Configuration
@EnableWebMvc
public class AuthConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new ResourceCodeBasedAuthInterceptor());
    }
}
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ResourceCode {

    String resourceCode();

}

在这个例子中,我们每个URL的Rest方法定义一个resourceCode来唯一标识它,然后在拦截器中,先加载出当前登录者所允许的全部resourceCode,只要当前方法的resourceCode被包含在所允许的全部resourceCode中,那么就证明当前用户可以访问。

至于如何获取当前登录者的所有resourceCode,我们可以通过如下的表设计来完成:

  • auth_user,存储所有用户信息
    • user_id
    • user_name
  • auth_role,存储所有角色信息
    • role_id
    • role_name
  • auth_user_role_relation,存储用户和角色的关系,一个用户可以拥有多个角色(多条记录)
    • relation_id
    • uder_id
    • role_id
  • auth_resource_code,存储资源信息
    • resource_id
    • resource_code
  • auth_role_resource_code_relation,存储角色和资源之间的关系,一个角色可以拥有多个资源(多条记录)
    • relation_id
    • role_id
    • resource_id

我们只要执行如下的SQL语句就能得到当前登录者所有的resourceCode:

select rc.resource_code 
from auth_user u 
left join auth_user_role_relation urr on u.user_id = urr.user_id 
left join auth_role_resource_code_relation rrcr on urr.role_id = rrcr.role_id
left join auth_resource_code rc on rrcr.resource_id = rc.resource_id
where u.user_name = ''

这种方案的优势是:

  • 对于角色很多的系统,我们不需要在代码中标注;
  • 如果后续需要新增一个角色,那么只需要在auth_role、auth_user_role_relation、auth_role_resource_code_relation中添加记录即可,不用去修改代码,改完立马就能生效;
  • 可以很方便地在系统运行期间调配不同人员、不同角色的权限信息。

相关文章

  • 一种垂直越权的解决方案

    垂直越权是一种非常常见且非常严重的权限漏洞,具体表现就是,低权限的用户可以不受控制的访问高权限用户的资源。 其实业...

  • 越权/逻辑漏洞

    一.本文介绍 1、本文介绍平行越权、垂直越权;登录、注册、修改密码、密码找回、支付等地方是否存在逻辑漏洞。 二.学...

  • 什么是越权访问漏洞?漏洞分类、开发层面理解!

    什么是越权访问漏洞? 越权访问(Broken Access Control,简称BAC)是Web应用程序中一种常见...

  • 垂直越权漏洞与代码分析

    一、前言 文章写完了之后,申请CVE有一些麻烦,不过好在还是申请到了 ps 申请CVE前,已经提交了CNVD 【一...

  • 元素居中解决方案

    html: css:1水平居中:解决方案1: 解决方案2: 解决方案3: 2垂直居中:解决方案1: 解决方案2: ...

  • day08 三种垂直水平居中 + 快捷写代码emmet + tr

    1 三种垂直水平居中方式 1.1第一种解决方案 1.2第二种解决方案 1.3第三种解决方案 2 快捷写代码emme...

  • 9.越权

    1、什么是越权a的权限小于b的权限,但是使用a用户的权限能够操作b用户的数据,叫做越权 2、越权漏洞分类水平越权和...

  • 《行政法》------ 具体行政行为一般原理

    一、具体行政行为的合法性要件 主体违法 1、无权限。2、纵向越权。3.横向越权(事物越权)4.地域越权 事实依据违...

  • 4.0用户模块开发(越权,高可复用的服务端响应类的封装)

    一.横向越权和纵向越权 横向越权:攻击者尝试访问与他拥有相同权限的用户资源; 纵向越权:低级别攻击者尝试访问高级别...

  • 越权

    横向越权:横向越权指的是攻击者尝试访问与他拥有相同权限的用户的资源 纵向越权:纵向越权指的是一个低级别攻击者尝试访...

网友评论

      本文标题:一种垂直越权的解决方案

      本文链接:https://www.haomeiwen.com/subject/vktzzktx.html