Apache-shiro-权限绕过漏洞汇总
字数 1872 2025-08-10 08:29:04
Apache Shiro 权限绕过漏洞分析与防护指南
漏洞概述
Apache Shiro 是一个功能强大且易用的 Java 安全框架,提供认证、授权、加密和会话管理等功能。本文档详细分析三个 Shiro 权限绕过漏洞(CVE-2020-1957、CVE-2020-11989、CVE-2020-13933),这些漏洞都利用了 Shiro 与 Spring 框架在 URL 解析处理上的差异,导致攻击者可以绕过权限控制访问受保护资源。
漏洞详情
CVE-2020-1957
漏洞原理
-
URL 处理流程:
- 用户请求格式:
/xxx/..;/hello/aaaa - Shiro 处理流程:
WebUtils#getPathWithinApplication调用getRequestUrigetRequestUri调用decodeAndCleanUriString处理 URIdecodeAndCleanUriString根据;截断,返回/xxx/..normalize方法处理路径规范化(替换..、.等)- 最终 Shiro 获取的路径为
/xxx/..,与权限配置/hello/**不匹配,绕过权限检查
- Spring 处理流程:
UrlPathHelper#getPathWithinServletMapping处理原始请求- 通过
request.getServletPath()获取/hello/aaaa - 最终访问到受保护资源
- 用户请求格式:
-
关键代码分析:
// WebUtils.java public static String getPathWithinApplication(HttpServletRequest request) { String contextPath = getContextPath(request); String requestUri = getRequestUri(request); if (StringUtils.startsWithIgnoreCase(requestUri, contextPath)) { String path = requestUri.substring(contextPath.length()); return (StringUtils.hasText(path) ? path : "/"); } else { return requestUri; } } // decodeAndCleanUriString 方法 private static String decodeAndCleanUriString(HttpServletRequest request, String uri) { uri = decodeRequestString(request, uri); int semicolonIndex = uri.indexOf(';'); return (semicolonIndex != -1 ? uri.substring(0, semicolonIndex) : uri); }
修复方案
- Shiro 1.5.2 版本修改了 URI 获取方式,从
getRequestURI改为getContextPath()、getServletPath()、getPathInfo()的组合
CVE-2020-11989
漏洞原理
-
两种利用方式:
-
方式一:
/hello/a%25%32%66a- Shiro 处理:
- URL 解码一次变为
/hello/a%2fa decodeRequestString再次解码变为/hello/a/a- 与权限配置
/hello/*不匹配(*不匹配多级路径)
- URL 解码一次变为
- Spring 处理为
/hello/a%2fa,匹配路由@GetMapping("/hello/{name}")
- Shiro 处理:
-
方式二:
/hello/a%25%32%66a/;/test/hello/aaa(需 context-path 不为空)- Shiro 处理:
- 解码后变为
/hello/a/a/ - 根据
;截断变为/hello/a/a/ - 与权限配置不匹配
- 解码后变为
- Spring 处理为
/test/hello/aaa
- Shiro 处理:
-
-
关键代码分析:
// WebUtils.java public static String getRequestUri(HttpServletRequest request) { String uri = (String) request.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE); if (uri == null) { uri = valueOrEmpty(request.getContextPath()) + "/" + valueOrEmpty(request.getServletPath()) + valueOrEmpty(request.getPathInfo()); } return normalize(decodeAndCleanUriString(request, uri)); } // decodeRequestString 方法 public static String decodeRequestString(HttpServletRequest request, String source) { String enc = determineEncoding(request); try { return URLDecoder.decode(source, enc); } catch (UnsupportedEncodingException ex) { return URLDecoder.decode(source); } }
修复方案
- Shiro 1.5.3 版本:
- 采用标准的
getServletPath和getPathInfo进行 URI 处理 - 取消了 URL 解码
public static String getPathWithinApplication(HttpServletRequest request) { return normalize(removeSemicolon(getServletPath(request) + getPathInfo(request))); } - 采用标准的
CVE-2020-13933
漏洞原理
-
利用方式:
- 请求格式:
/hello/%3baaaa - Shiro 处理:
- URL 解码为
/hello/;aaaa removeSemicolon根据;截断,返回/hello/- 与权限配置
/hello/*不匹配
- URL 解码为
- Spring 处理为原始路径,匹配路由
- 请求格式:
-
关键代码:
private static String removeSemicolon(String uri) { int semicolonIndex = uri.indexOf(';'); return (semicolonIndex != -1 ? uri.substring(0, semicolonIndex) : uri); }
修复方案
- 新增
InvalidRequestFilter过滤器,检查特殊字符:private static final List<String> SEMICOLON = Collections.unmodifiableList(Arrays.asList(";", "%3b", "%3B")); private static final List<String> BACKSLASH = Collections.unmodifiableList(Arrays.asList("\\", "%5c", "%5C"));
漏洞防护建议
-
升级方案:
- 及时升级到最新版本 Shiro
- CVE-2020-1957:升级至 1.5.2+
- CVE-2020-11989:升级至 1.5.3+
- CVE-2020-13933:升级至 1.6.0+
-
临时缓解措施:
- 在应用中添加过滤器,对请求路径进行严格校验
- 禁用分号(
;)和反斜杠(\)等特殊字符 - 统一 Shiro 和 Spring 的 URL 解析逻辑
-
安全配置建议:
- 使用严格路径匹配模式
- 避免使用过于宽松的通配符(如
**) - 实施多层防御,结合框架安全机制和自定义校验
总结
这三个 Shiro 权限绕过漏洞的核心问题在于 Shiro 和 Spring 对 URL 的解析处理存在差异,攻击者利用这些差异构造特殊路径绕过权限检查。防护的关键在于:
- 统一 URL 解析逻辑
- 严格控制特殊字符处理
- 保持框架最新版本
- 实施深度防御策略
通过理解这些漏洞的原理和修复方案,开发人员可以更好地保护应用免受类似攻击。