基于自定义鉴权的JAVA权限绕过
字数 3521 2025-11-21 12:35:29

Java Web应用权限绕过漏洞分析与防护指南

0x00 前言

背景概述

在国内Web项目中,开发者常使用过滤器(Filter)或拦截器(Interceptor)等自定义方法实现统一鉴权。然而,由于对请求路径获取API的错误使用或混用,导致权限校验存在安全盲点。攻击者可利用路径解析差异构造特殊请求绕过鉴权机制。

漏洞影响范围

  • 影响组件:某微、某凌、某远等主流OA/CMS系统
  • 技术根源:Spring框架路径解析特性与自定义鉴权逻辑不匹配
  • 实战价值:攻防演练中高频出现的高危漏洞类型

0x01 常见鉴权方案分析

Java鉴权方案对比表

| 框架/方式 | 特性 | 基于什么鉴权 | 适用场景 |
|---------|------|------------|---------|
| 自定义Servlet Filter | 全局请求入口、可拦截静态资源 | Session/Token(自解析) | 自定义认证逻辑、中央鉴权 |
| Spring HandlerInterceptor | 控制器级拦截、路由粒度 | 依赖上游认证 | URL级鉴权、审计 |
| AOP自定义方法拦截 | 业务方法最靠近,支持复杂上下文 | 基于ThreadLocal/Token的主体信息 | 细粒度业务授权 |
| Spring Security | 完整过滤器链、方法级注解 | Session/JWT/OAuth2 tokens | 现代Spring Boot应用 |
| Apache Shiro | 简洁Subject/Realm/Session | Session/RememberMe/Token | 轻量应用、非Spring项目 |

自定义鉴权实现详解

1. Servlet Filter实现

配置类:

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Bean
    public FilterRegistrationBean<VulnerableAuthFilter> vulnerableAuthFilter() {
        FilterRegistrationBean<VulnerableAuthFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new VulnerableAuthFilter());
        registrationBean.addUrlPatterns("/admin/*");
        return registrationBean;
    }
}

过滤器逻辑:

@WebFilter("/*")
public class AuthFilter implements Filter {
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        
        String authHeader = httpRequest.getHeader("Authorization");
        if (authHeader == null || !authHeader.equals("BypassVulnDemo")) {
            httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            httpResponse.getWriter().write("Filter: Authentication required");
            return;
        }
        chain.doFilter(request, response);
    }
}

2. Spring MVC拦截器实现

配置类:

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new VulnerableInterceptor())
                .addPathPatterns("/secure/**");
    }
}

拦截器逻辑:

public class AuthzInterceptor implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) {
        String authHeader = request.getHeader("Authorization");
        if (authHeader == null || !authHeader.equals("BypassVulnDemo")) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.getWriter().write("Unauthorized: Authentication required");
            return false;
        }
        return true;
    }
}

3. AOP切面实现

自定义注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequireAuth {
}

切面逻辑:

@Aspect
@Component
public class AopConfig {
    @Around("@annotation(com.bypassvuln.config.RequireAuth)")
    public Object checkAuth(ProceedingJoinPoint joinPoint) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getResponse();
        
        String authHeader = request.getHeader("Authorization");
        if (authHeader == null || !authHeader.equals("BypassVulnDemo")) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.getWriter().write("Unauthorized: Authentication required");
            return null;
        }
        return joinPoint.proceed();
    }
}

0x02 请求路径获取方法分析

路径获取API对比表(Spring-Web 5.3.20)

| 类别 | 方法 | 描述 | 安全特性 |
|------|------|------|----------|
| HttpServletRequest | getRequestURL() | 返回完整URL | 原始输入,无处理 |
| | getRequestURI() | 返回协议名到查询字符串间部分 | 原始输入,无处理 |
| | getServletPath() | 返回servlet路径 | 删除;后内容、URL解码、解析../ |
| | getPathInfo() | 返回额外路径信息 | 一般为null |
| Spring HandlerMapping | BEST_MATCHING_PATTERN_ATTRIBUTE | 返回Controller配置路径 | 强关联匹配资源 |
| | PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE | 处理器映射内路径 | 删除;后内容,不解码 |
| Spring UrlPathHelper | getPathWithinApplication() | 应用内路径 | 删除;后内容、URL解码 |

路径解析示例

请求:http://localhost:18083/app/path-demo/show///%2e%2e/../admin;.js

解析结果:

{
    "getRequestURL()": "http://localhost:18083/app/path-demo/show///%2e%2e/../admin/dashboard;.js",
    "getRequestURI()": "/app/path-demo/show///%2e%2e/../admin/dashboard;.js", 
    "getServletPath()": "/admin/dashboard",
    "HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE": "/path-demo/show/**",
    "HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE": "/path-demo/show///%2e%2e/../admin/dashboard"
}

0x03 权限绕过技术深度分析

场景一:startsWith匹配绕过

漏洞代码:

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    String requestUri = request.getRequestURI();
    
    // 漏洞:使用startsWith匹配,可被../绕过
    if (requestUri.startsWith("/swagger")) {
        return true;
    }
    // 鉴权逻辑...
}

绕过Payload:

  • /swagger/../app/secure/data
  • /swagger/..;666666/app/secure/data
  • /swagger/////%2e%2e;666666/////%61%70%70/secure/data

技术原理:

  • getRequestURI()返回原始未规范化路径
  • Tomcat的CoyoteRequest保存原始请求对象
  • Spring MVC在HandlerMapping阶段进行路径规范化
  • 上下文路径配置影响路径解析结果

场景二:endsWith匹配绕过

漏洞代码:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    String requestUrl = httpRequest.getRequestURL().toString();
    
    // 漏洞:使用endsWith匹配,可被;.js绕过
    if (requestUrl.endsWith(".js")) {
        chain.doFilter(request, response);
        return;
    }
    // 鉴权逻辑...
}

绕过Payload:

  • /app/admin/dashboard;.js
  • /app/admin;666666/dashboard;6666666;;;.js

技术原理:

  • 分号;标识矩阵参数,Spring会删除;/间内容
  • /时删除;后所有内容
  • 规范化后仍能匹配到目标Controller

场景三:contains匹配绕过

漏洞代码:

@Around("@annotation(com.bypassvuln.config.RequireAuth)")
public Object checkAuth(ProceedingJoinPoint joinPoint) throws Throwable {
    String requestUri = request.getRequestURI();
    
    // 漏洞:使用contains匹配
    if (requestUri.contains("swagger")) {
        return joinPoint.proceed();
    }
    // 鉴权逻辑...
}

绕过Payload:

  • /swagger/../app/api/sensitive
  • /app/api/sensitive;swagger
  • /app/api;swagger/sensitive

场景四:后缀匹配模式绕过(历史漏洞)

背景:

  • Spring 5.3之前默认开启useSuffixPatternMatch
  • 5.3开始默认关闭,需手动配置

配置开启:

@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
    configurer.setUseSuffixPatternMatch(true);
}

绕过效果:

  • /upload.swagger等效于/upload

场景五:强制性白名单绕过

漏洞代码:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    String requestUri = httpRequest.getRequestURI();
    
    // 漏洞:强制性检查.do/.jsp后缀
    if (requestUri.endsWith(".do") || requestUri.endsWith(".jsp")) {
        // 执行鉴权
    } else {
        // 直接放行
        chain.doFilter(request, response);
    }
}

绕过Payload:

  • /system/dashboard.do/(结尾加/)
  • /system/dashboard.do;(结尾加分号)

场景六:equals精确匹配绕过

漏洞代码:

String requestUri = httpRequest.getRequestURI();
if (requestUri.equals("/app/system/admin")) {
    // 执行鉴权
}

绕过Payload:

  • URL编码:/app/system/%61%64%6d%69%6e

Spring 5.3.20路径解析特性总结

| 特性 | context-path | controller-path | 说明 |
|------|-------------|-----------------|------|
| ;参数处理 | 支持 | 支持 | 删除;至/间内容 |
| ../解析 | 支持 | 不支持 | 需配置context-path |
| URL解码 | 支持 | 支持 | 规范化阶段处理 |
| 多个/处理 | 不支持 | 不支持 | 保持原样 |

0x04 安全防护机制

Spring Security StrictHttpFirewall

默认防护规则

  1. HTTP方法检查:仅允许GET/POST/PUT/PATCH/DELETE/HEAD/OPTIONS
  2. URL阻止列表
    • 编码类禁止:%25%2e%2E
    • 字符类禁止:%\u2028\u2029;%2f//\%00
  3. 路径规范化检查:检测...等非规范化路径
  4. 非打印字符检测:拒绝包含非打印ASCII字符的请求

防护效果示例

引入依赖即可启用:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

自定义配置(不推荐生产环境)

@Bean
public HttpFirewall relaxedFirewall() {
    StrictHttpFirewall firewall = new StrictHttpFirewall();
    firewall.setAllowSemicolon(true);
    firewall.setAllowUrlEncodedSlash(true);
    firewall.setAllowUrlEncodedPeriod(true);
    return firewall;
}

安全开发建议

1. 使用一致的路径获取API

  • 鉴权阶段使用HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE
  • 避免混用getRequestURI()getServletPath()

2. 路径规范化处理

private String normalizePath(String path) {
    return UriUtils.decode(path, StandardCharsets.UTF_8)
                   .replace("//", "/")
                   .replace("/../", "/")
                   .replace("/./", "/");
}

3. 使用Spring Security最佳实践

  • 优先使用Spring Security而非自定义鉴权
  • 保持StrictHttpFirewall默认配置
  • 使用方法级注解@PreAuthorize进行细粒度控制

4. 安全测试用例

@Test
public void testPathBypassVulnerabilities() {
    // 测试各种绕过Payload
    String[] testPaths = {
        "/admin/../secure/data",
        "/api/sensitive;swagger",
        "/system/dashboard.do/",
        "/app/system/%61%64%6d%69%6e"
    };
    
    for (String path : testPaths) {
        mockMvc.perform(get(path))
               .andExpect(status().isForbidden());
    }
}

漏洞检测清单

  1. 代码审计重点

    • 查找自定义Filter/Interceptor中使用getRequestURI()startsWith()endsWith()contains()的代码
    • 检查路径匹配逻辑是否进行规范化处理
    • 确认是否使用Spring Security及其配置
  2. 黑盒测试Payload

    • 目录穿越:/path/../target/path/..;/target
    • 分号参数:/target;bypass/target;/bypass
    • URL编码:/%74%61%72%67%65%74
    • 后缀变异:/target.js//target.jsp/

总结

Java Web应用权限绕过漏洞源于路径解析差异与鉴权逻辑不匹配。深入理解Spring框架路径处理机制、严格统一路径获取方式、采用安全框架默认防护是有效防护的关键。开发人员应避免使用字符串匹配进行路径鉴权,安全人员需掌握各种绕过技术以进行全面安全测试。

Java Web应用权限绕过漏洞分析与防护指南 0x00 前言 背景概述 在国内Web项目中,开发者常使用过滤器(Filter)或拦截器(Interceptor)等自定义方法实现统一鉴权。然而,由于对请求路径获取API的错误使用或混用,导致权限校验存在安全盲点。攻击者可利用路径解析差异构造特殊请求绕过鉴权机制。 漏洞影响范围 影响组件:某微、某凌、某远等主流OA/CMS系统 技术根源:Spring框架路径解析特性与自定义鉴权逻辑不匹配 实战价值:攻防演练中高频出现的高危漏洞类型 0x01 常见鉴权方案分析 Java鉴权方案对比表 | 框架/方式 | 特性 | 基于什么鉴权 | 适用场景 | |---------|------|------------|---------| | 自定义Servlet Filter | 全局请求入口、可拦截静态资源 | Session/Token(自解析) | 自定义认证逻辑、中央鉴权 | | Spring HandlerInterceptor | 控制器级拦截、路由粒度 | 依赖上游认证 | URL级鉴权、审计 | | AOP自定义方法拦截 | 业务方法最靠近,支持复杂上下文 | 基于ThreadLocal/Token的主体信息 | 细粒度业务授权 | | Spring Security | 完整过滤器链、方法级注解 | Session/JWT/OAuth2 tokens | 现代Spring Boot应用 | | Apache Shiro | 简洁Subject/Realm/Session | Session/RememberMe/Token | 轻量应用、非Spring项目 | 自定义鉴权实现详解 1. Servlet Filter实现 配置类: 过滤器逻辑: 2. Spring MVC拦截器实现 配置类: 拦截器逻辑: 3. AOP切面实现 自定义注解: 切面逻辑: 0x02 请求路径获取方法分析 路径获取API对比表(Spring-Web 5.3.20) | 类别 | 方法 | 描述 | 安全特性 | |------|------|------|----------| | HttpServletRequest | getRequestURL() | 返回完整URL | 原始输入,无处理 | | | getRequestURI() | 返回协议名到查询字符串间部分 | 原始输入,无处理 | | | getServletPath() | 返回servlet路径 | 删除;后内容、URL解码、解析../ | | | getPathInfo() | 返回额外路径信息 | 一般为null | | Spring HandlerMapping | BEST_ MATCHING_ PATTERN_ ATTRIBUTE | 返回Controller配置路径 | 强关联匹配资源 | | | PATH_ WITHIN_ HANDLER_ MAPPING_ ATTRIBUTE | 处理器映射内路径 | 删除;后内容,不解码 | | Spring UrlPathHelper | getPathWithinApplication() | 应用内路径 | 删除;后内容、URL解码 | 路径解析示例 请求: http://localhost:18083/app/path-demo/show///%2e%2e/../admin;.js 解析结果: 0x03 权限绕过技术深度分析 场景一:startsWith匹配绕过 漏洞代码: 绕过Payload: /swagger/../app/secure/data /swagger/..;666666/app/secure/data /swagger/////%2e%2e;666666/////%61%70%70/secure/data 技术原理: getRequestURI() 返回原始未规范化路径 Tomcat的 CoyoteRequest 保存原始请求对象 Spring MVC在 HandlerMapping 阶段进行路径规范化 上下文路径配置影响路径解析结果 场景二:endsWith匹配绕过 漏洞代码: 绕过Payload: /app/admin/dashboard;.js /app/admin;666666/dashboard;6666666;;;.js 技术原理: 分号 ; 标识矩阵参数,Spring会删除 ; 至 / 间内容 无 / 时删除 ; 后所有内容 规范化后仍能匹配到目标Controller 场景三:contains匹配绕过 漏洞代码: 绕过Payload: /swagger/../app/api/sensitive /app/api/sensitive;swagger /app/api;swagger/sensitive 场景四:后缀匹配模式绕过(历史漏洞) 背景: Spring 5.3之前默认开启 useSuffixPatternMatch 5.3开始默认关闭,需手动配置 配置开启: 绕过效果: /upload.swagger 等效于 /upload 场景五:强制性白名单绕过 漏洞代码: 绕过Payload: /system/dashboard.do/ (结尾加/) /system/dashboard.do; (结尾加分号) 场景六:equals精确匹配绕过 漏洞代码: 绕过Payload: URL编码: /app/system/%61%64%6d%69%6e Spring 5.3.20路径解析特性总结 | 特性 | context-path | controller-path | 说明 | |------|-------------|-----------------|------| | ; 参数处理 | 支持 | 支持 | 删除;至/间内容 | | ../ 解析 | 支持 | 不支持 | 需配置context-path | | URL解码 | 支持 | 支持 | 规范化阶段处理 | | 多个 / 处理 | 不支持 | 不支持 | 保持原样 | 0x04 安全防护机制 Spring Security StrictHttpFirewall 默认防护规则 HTTP方法检查 :仅允许GET/POST/PUT/PATCH/DELETE/HEAD/OPTIONS URL阻止列表 : 编码类禁止: %25 、 %2e 、 %2E 字符类禁止: % 、 \u2028 、 \u2029 、 ; 、 %2f 、 // 、 \ 、 %00 等 路径规范化检查 :检测 .. 、 . 等非规范化路径 非打印字符检测 :拒绝包含非打印ASCII字符的请求 防护效果示例 引入依赖即可启用: 自定义配置(不推荐生产环境) 安全开发建议 1. 使用一致的路径获取API 鉴权阶段使用 HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE 避免混用 getRequestURI() 和 getServletPath() 2. 路径规范化处理 3. 使用Spring Security最佳实践 优先使用Spring Security而非自定义鉴权 保持StrictHttpFirewall默认配置 使用方法级注解 @PreAuthorize 进行细粒度控制 4. 安全测试用例 漏洞检测清单 代码审计重点 : 查找自定义Filter/Interceptor中使用 getRequestURI() 、 startsWith() 、 endsWith() 、 contains() 的代码 检查路径匹配逻辑是否进行规范化处理 确认是否使用Spring Security及其配置 黑盒测试Payload : 目录穿越: /path/../target 、 /path/..;/target 分号参数: /target;bypass 、 /target;/bypass URL编码: /%74%61%72%67%65%74 后缀变异: /target.js/ 、 /target.jsp/ 总结 Java Web应用权限绕过漏洞源于路径解析差异与鉴权逻辑不匹配。深入理解Spring框架路径处理机制、严格统一路径获取方式、采用安全框架默认防护是有效防护的关键。开发人员应避免使用字符串匹配进行路径鉴权,安全人员需掌握各种绕过技术以进行全面安全测试。