Shiro从入门到权限绕过漏洞
字数 1859 2025-08-24 16:48:15

Apache Shiro 从入门到权限绕过漏洞分析

一、Shiro 简介

Apache Shiro 是一个强大且易用的 Java 安全框架,用于身份验证、授权、加密和会话管理。它可以用于保护任何应用程序,从命令行应用程序、移动应用程序到大型的 Web 和企业应用程序。

Shiro 的核心架构

Shiro 架构由以下几个核心组件组成:

  1. Subject:代表当前用户或当前程序,是一个接口,定义了认证和授权的方法

    • 认证:判断用户是否为合法用户的过程
    • 授权:认证成功后分配权限,决定用户可以访问哪些资源
  2. SecurityManager:安全管理器,负责认证和授权

    • 通过 Authenticator 认证器进行认证
    • 通过 Authorizer 授权器进行授权
    • 通过 SessionManager 会话管理器进行会话管理
  3. Authenticator:认证器,负责身份认证

    • 从 Realm 获取用户数据进行身份验证
  4. Authorizer:授权器,判断用户身份拥有的权限

  5. Realm:数据源,相当于数据库

    • 从数据库(如 MySQL)获取用户信息
    • 包含认证和授权相关操作
  6. SessionManager:会话管理器

    • 不依赖 Web 容器的 session
    • 可用于非 Web 应用
    • 支持分布式应用的会话集中管理
  7. SessionDAO:会话存储

    • 可通过 JDBC 将会话存储到数据库

二、Shiro 认证流程

认证基本概念

  • 身份信息 (Principal):用户名、邮箱等标识信息
  • 凭据信息 (Credential):密码、证书等

认证流程

  1. 用户携带身份信息和凭据信息(用户名和密码)
  2. Shiro 将用户名和密码封装成 Token
  3. 通过 SecurityManager 安全管理器
  4. SecurityManager 调用 Authenticator 认证器
  5. Authenticator 调用 Realm 获取数据并进行比对
  6. 比对成功则认证成功,否则认证失败

认证代码示例

// 1. 创建安全管理器对象
DefaultSecurityManager securityManager = new DefaultSecurityManager();

// 2. 给安全管理器设置realm
securityManager.setRealm(new IniRealm("classpath:shiro.ini"));

// 3. SecurityUtils 给全局安全工具类设置安全管理器
SecurityUtils.setSecurityManager(securityManager);

// 4. 获取主体 Subject
Subject subject = SecurityUtils.getSubject();

// 5. 创建令牌
UsernamePasswordToken token = new UsernamePasswordToken("relaysec", "123456");

try {
    subject.login(token); // 用户认证
    System.out.println("登录成功");
} catch (UnknownAccountException e) {
    System.out.println("认证失败: 用户名不存在~");
} catch (IncorrectCredentialsException e) {
    System.out.println("认证失败: 密码错误~");
}

认证源码分析

  1. 用户名认证

    • 调用 Subject.login() 方法
    • 实际调用 DefaultSecurityManager.login()
    • 调用 authenticator.authenticate()
    • 最终在 SimpleAccountRealm.doGetAuthenticationInfo() 中实现用户名处理
  2. 密码认证

    • 调用 assertCredentialsMatch() 方法
    • 使用密码匹配器进行密码比对
    • 默认使用 equals 方法比对
    • 如果密码加密或加盐,会有其他处理方式

三、Shiro 授权

授权流程

  1. 用户认证成功后登录系统
  2. 判断用户是否有权限访问请求的资源
  3. 有权限则允许访问,否则拒绝访问

四、Spring Boot 整合 Shiro

依赖配置

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-web</artifactId>
    <version>1.7.0</version>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.7.0</version>
</dependency>

Shiro 配置类

@Configuration
public class ShiroConfig {
    
    // 1. 创建ShiroFilter
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        
        // 配置系统资源权限
        Map<String, String> map = new HashMap<>();
        map.put("/admin/**", "anon");  // 公共资源
        map.put("/admin/users", "authc");  // 需要认证和授权
        map.put("/demo/**", "anon");
        map.put("/index.jsp", "authc");
        
        shiroFilterFactoryBean.setLoginUrl("/login.jsp");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }
    
    // 2. 创建安全管理器
    @Bean
    public DefaultWebSecurityManager getDefaultWebSecurityManager(Realm realm) {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(realm);
        return defaultWebSecurityManager;
    }
    
    // 3. 创建自定义Realm
    @Bean
    public Realm getRealm() {
        CustomerRealm customerRealm = new CustomerRealm();
        return customerRealm;
    }
}

自定义 Realm 示例

public class CustomerRealm extends AuthorizingRealm {
    
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }
    
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String principal = (String) token.getPrincipal();
        String password_db = "123";
        String username_db = "zhangsan";
        
        if (username_db.equals(principal)) {
            return new SimpleAuthenticationInfo(principal, "123", this.getName());
        }
        return null;
    }
}

五、Shiro 过滤流程分析

  1. 请求到达 Tomcat,以责任链形式调用 Filter
  2. OncePerRequestFilter 调用 doFilterInternal
  3. PathMatchingFilterChainResolver.getChain 处理过滤
    • 获取请求路径
    • 进行路径标准化处理
    • 与配置的权限规则进行匹配
  4. 匹配成功后创建 ProxiedFilterChain 代理对象处理请求

六、Shiro 权限绕过漏洞分析

CVE-2020-1957

环境要求

  • Spring Boot: 2.2.6.RELEASE
  • Shiro: 1.5.0

漏洞原理

  • Shiro 和 Spring 对 URL 分号处理不一致
  • Shiro 不会过滤分号,而 Spring 会过滤分号
  • 导致权限检查绕过

漏洞复现

  • 配置权限规则:
    map.put("/admin/*", "authc");  // 需要认证
    map.put("/demo/**", "anon");   // 公共资源
    
  • 绕过方式:/demo/..;/admin/index

漏洞分析

  1. Shiro 处理:

    • 获取请求路径 //demo/..;/admin/users
    • 截取分号前部分://demo/..
    • 标准化处理后:/demo/..
    • 匹配 /demo/** 规则,允许访问
  2. Spring 处理:

    • 过滤掉分号,实际路径变为 /demo/../admin/users
    • 标准化处理后:/admin/users
    • 最终访问的是受保护的 /admin/users 资源

修复建议

  • 升级 Shiro 到最新版本
  • 避免使用过于宽松的 URL 匹配规则
  • 对关键资源实施多重保护机制
Apache Shiro 从入门到权限绕过漏洞分析 一、Shiro 简介 Apache Shiro 是一个强大且易用的 Java 安全框架,用于身份验证、授权、加密和会话管理。它可以用于保护任何应用程序,从命令行应用程序、移动应用程序到大型的 Web 和企业应用程序。 Shiro 的核心架构 Shiro 架构由以下几个核心组件组成: Subject :代表当前用户或当前程序,是一个接口,定义了认证和授权的方法 认证:判断用户是否为合法用户的过程 授权:认证成功后分配权限,决定用户可以访问哪些资源 SecurityManager :安全管理器,负责认证和授权 通过 Authenticator 认证器进行认证 通过 Authorizer 授权器进行授权 通过 SessionManager 会话管理器进行会话管理 Authenticator :认证器,负责身份认证 从 Realm 获取用户数据进行身份验证 Authorizer :授权器,判断用户身份拥有的权限 Realm :数据源,相当于数据库 从数据库(如 MySQL)获取用户信息 包含认证和授权相关操作 SessionManager :会话管理器 不依赖 Web 容器的 session 可用于非 Web 应用 支持分布式应用的会话集中管理 SessionDAO :会话存储 可通过 JDBC 将会话存储到数据库 二、Shiro 认证流程 认证基本概念 身份信息 (Principal) :用户名、邮箱等标识信息 凭据信息 (Credential) :密码、证书等 认证流程 用户携带身份信息和凭据信息(用户名和密码) Shiro 将用户名和密码封装成 Token 通过 SecurityManager 安全管理器 SecurityManager 调用 Authenticator 认证器 Authenticator 调用 Realm 获取数据并进行比对 比对成功则认证成功,否则认证失败 认证代码示例 认证源码分析 用户名认证 : 调用 Subject.login() 方法 实际调用 DefaultSecurityManager.login() 调用 authenticator.authenticate() 最终在 SimpleAccountRealm.doGetAuthenticationInfo() 中实现用户名处理 密码认证 : 调用 assertCredentialsMatch() 方法 使用密码匹配器进行密码比对 默认使用 equals 方法比对 如果密码加密或加盐,会有其他处理方式 三、Shiro 授权 授权流程 用户认证成功后登录系统 判断用户是否有权限访问请求的资源 有权限则允许访问,否则拒绝访问 四、Spring Boot 整合 Shiro 依赖配置 Shiro 配置类 自定义 Realm 示例 五、Shiro 过滤流程分析 请求到达 Tomcat,以责任链形式调用 Filter OncePerRequestFilter 调用 doFilterInternal PathMatchingFilterChainResolver.getChain 处理过滤 获取请求路径 进行路径标准化处理 与配置的权限规则进行匹配 匹配成功后创建 ProxiedFilterChain 代理对象处理请求 六、Shiro 权限绕过漏洞分析 CVE-2020-1957 环境要求 : Spring Boot: 2.2.6.RELEASE Shiro: 1.5.0 漏洞原理 : Shiro 和 Spring 对 URL 分号处理不一致 Shiro 不会过滤分号,而 Spring 会过滤分号 导致权限检查绕过 漏洞复现 : 配置权限规则: 绕过方式: /demo/..;/admin/index 漏洞分析 : Shiro 处理: 获取请求路径 //demo/..;/admin/users 截取分号前部分: //demo/.. 标准化处理后: /demo/.. 匹配 /demo/** 规则,允许访问 Spring 处理: 过滤掉分号,实际路径变为 /demo/../admin/users 标准化处理后: /admin/users 最终访问的是受保护的 /admin/users 资源 修复建议 : 升级 Shiro 到最新版本 避免使用过于宽松的 URL 匹配规则 对关键资源实施多重保护机制