Shiro认证绕过漏洞深度解析:从CVE-2010-3863到CVE-2020-1957的路径处理逻辑剖析 1. 项目概述一次对Shiro认证逻辑的深度“外科手术”在Java Web应用安全领域Apache Shiro是一个绕不开的名字。它以其简洁、易用的特性成为了众多开发者进行身份认证、授权、加密和会话管理的首选安全框架。然而正如任何复杂的软件系统一样Shiro在追求功能强大的同时其内部复杂的URL路径处理逻辑也潜藏着风险。今天我们不谈泛泛的安全概念而是拿起“手术刀”深入剖析两个极具代表性的Shiro认证绕过漏洞CVE-2010-3863和CVE-2020-1957。这两个漏洞的触发点看似简单——一个是路径中的“/./”另一个是URL末尾的“”分号——但它们却像两把精准的钥匙巧妙地绕过了Shiro精心构建的认证防线直击其路径匹配逻辑的核心缺陷。理解这两个漏洞远不止是记住两个CVE编号和Payload那么简单。它是一次对Shiro过滤器链、Ant风格路径匹配器、以及Servlet容器如Tomcat默认行为之间微妙交互的绝佳学习机会。对于安全研究人员这是挖掘逻辑漏洞的经典案例对于开发人员这是理解框架“黑盒”、编写更健壮代码的必修课对于运维和架构师这是审视现有系统安全水位、制定有效修复策略的关键参考。我们将从漏洞的触发现象出发层层剥开表象直抵其底层逻辑根源并最终给出从代码层到架构层的立体化修复与防御方案。这趟旅程将让你对Web安全有一个更深刻、更落地的认识。2. 漏洞背景与核心概念解析在深入漏洞细节之前我们必须先搭建起必要的基础知识舞台。Shiro的安全模型核心是它的过滤器链。当你在web.xml或通过Shiro的ShiroFilterFactoryBean在Spring环境中配置了Shiro后所有到达应用的请求都会首先经过一个名为ShiroFilter的入口。2.1 Shiro过滤器链与路径匹配这个过滤器的核心工作之一就是根据当前请求的URL决定应该应用哪一条安全规则。这些规则在Shiro的INI配置文件或Java配置中定义通常形如[urls] /index.html anon /admin/** authc, roles[admin] /api/** authc /** authc这里的/admin/**就是一个典型的Ant风格路径表达式。authc表示需要认证anon表示可以匿名访问。当请求到来时Shiro会拿着请求的URI注意是经过Servlet容器处理后的request.getServletPath()或request.getPathInfo()的组合具体取决于配置按照配置列表中从上到下的顺序与这些Ant模式进行匹配。一旦找到第一个匹配的模式就应用该模式对应的过滤器链后续的模式不再检查。这里就引出了第一个关键点匹配的顺序性和首次匹配原则。如果你的配置是/** authc写在最前面那么所有请求都会被要求认证后面的规则就形同虚设了。因此规则的顺序至关重要。2.2 Ant风格路径匹配的“陷阱”Ant匹配规则很强大支持?单字符、*单层路径、**多层路径。但它的匹配逻辑是基于规范化后的路径字符串进行的。所谓规范化通常包括将//合并为/解析.当前目录和..上级目录等。然而规范化发生的位置和时机成为了漏洞滋生的温床。2.3 Servlet容器的路径解析另一个关键角色是Servlet容器如Tomcat、Jetty。容器在将请求交给Shiro或者说你的Web应用之前会对请求的URI进行自己的解析和处理。例如Tomcat默认会解码URL编码并进行一定程度的路径规范化。但容器对某些特殊字符的处理方式可能与Shiro的预期存在差异这种差异就是“缝隙”。理解了这三个角色Shiro过滤器链、Ant路径匹配器、Servlet容器及其交互我们就可以开始审视那两个经典的漏洞了。它们本质上都是利用了路径信息在传递和匹配过程中在不同组件间的不一致从而让Shiro的匹配逻辑“看错了”目标路径匹配到了一个权限更宽松的规则上。3. CVE-2010-3863被“/./”撕裂的防线这是Shiro早期一个非常经典的目录遍历型认证绕过漏洞。它的影响范围是Apache Shiro 1.0.0之前的版本是的这是一个上古漏洞但原理永不过时。3.1 漏洞触发场景还原假设我们有一个简单的Web应用其Shiro安全配置如下[urls] /public/** anon /secure/** authc我们的目标是访问一个受保护的资源/secure/admin.jsp但我们没有登录按道理会被重定向到登录页。攻击者构造了这样一个请求GET /secure/./admin.jsp HTTP/1.1 Host: vulnerable-app.com或者使用URL编码的形式GET /secure/%2e%2f/admin.jsp HTTP/1.1 Host: vulnerable-app.com神奇的事情发生了用户在没有登录的情况下直接看到了/secure/admin.jsp的内容。3.2 底层逻辑深度拆解这个漏洞的根源在于Shiro的路径匹配逻辑与Servlet容器路径规范化逻辑的顺序错位。请求进入攻击者发送请求/secure/./admin.jsp。容器处理Servlet容器如Tomcat接收到请求后会首先对请求URI进行规范化Normalization。规范化的规则之一就是解析路径中的.和..。/./在Unix和URL路径中代表“当前目录”。因此容器会将/secure/./admin.jsp规范化为/secure/admin.jsp。这是符合RFC标准的正确行为。Shiro获取路径漏洞版本的Shiro在获取请求路径用于匹配时可能没有直接使用容器规范化后的路径/secure/admin.jsp或者是在匹配逻辑的某个环节错误地处理了包含.的路径。一种典型的情况是Shiro的路径匹配器在匹配前没有进行与容器同等严格的规范化或者规范化规则不同。路径匹配错位关键在于Shiro配置的匹配顺序。它拿着可能还未被完全规范化的路径或经过不同规则处理后的路径去匹配/public/**和/secure/**。当路径是/secure/./admin.jsp时Ant匹配器可能不认为它匹配/secure/**因为/secure/./admin.jsp这个字符串在简单的字符串匹配或某些Ant实现看来并不完全符合/secure/**的模式**期望的是/secure/后直接跟内容而这里跟的是./。由于/secure/./admin.jsp没有匹配到/secure/**匹配过程继续向下。在很多默认配置中最后会有一个兜底的规则比如/** anon允许所有匿名访问。于是这个请求就匹配到了匿名访问规则上绕过了认证。请求最终路由尽管Shiro因为匹配错误而放行了请求但当请求真正被路由到Servlet或JSP进行处理时容器使用的已经是规范化后的路径/secure/admin.jsp。因此应用最终正确返回了受保护资源的内容造成了权限绕过。核心要点漏洞的本质是路径规范化的一致性问题。Shiro用于做安全决策的路径和最终处理请求的路径在.和..的解析上出现了分歧。安全框架“看”到的路径认为不安全但匹配了宽松规则和应用程序实际执行的路径受保护资源不是同一个。3.3 修复方案与代码层面的思考Apache Shiro在后续版本中修复了此漏洞。修复的核心思想是确保Shiro用于路径匹配的URI是经过完全、正确规范化后的URI并且与Servlet容器处理请求时使用的URI保持一致。具体修复措施包括统一使用HttpServletRequest的标准方法在PathMatchingFilterShiro中负责URL匹配的过滤器基类中修复代码确保通过request.getServletPath()和request.getPathInfo()来获取路径并对其进行合并和规范化。这两个方法返回的是容器已经处理过的路径。强化路径规范化函数Shiro内部增强了路径规范化逻辑确保在处理路径匹配前能正确移除/./..这样的序列使其标准化。例如实现一个类似org.apache.shiro.web.util.WebUtils#getPathWithinApplication的方法它负责返回一个规范化后的、用于匹配的请求路径。配置层面的防御开发者应避免使用过于宽泛的兜底规则如/** anon。更安全的做法是采用“默认拒绝”策略明确列出所有允许匿名访问的路径最后用/** authc要求认证所有其他请求。这样即使匹配逻辑出现偏差未明确放行的路径默认也是受保护的。给开发者的启示在编写任何与路径处理相关的代码时不仅是安全框架都要明确一点路径字符串必须在使用前进行规范化。Java中可以使用java.nio.file.Path#normalize()或org.springframework.util.StringUtils#cleanPathSpring提供等工具方法。永远不要相信用户输入的原始路径。4. CVE-2020-1957分号“”带来的意外之旅时间来到2020年Shiro再次曝出一个认证绕过漏洞影响版本为Apache Shiro 1.5.2。这个漏洞的原理与CVE-2010-3863异曲同工但利用的“道具”换成了分号;。4.1 漏洞触发场景还原同样的安全配置[urls] /public/** anon /secure/** authc攻击者这次构造的请求是GET /secure/admin.jsp;xxx HTTP/1.1 Host: vulnerable-app.com或者GET /secure/;xxx/admin.jsp HTTP/1.1 Host: vulnerable-app.com这里的xxx可以是任意字符比如jsessionidABCD虽然这不是标准的Session ID传递方式或者干脆就是;test。结果再次令人意外认证被绕过/secure/admin.jsp被直接访问。4.2 底层逻辑深度拆解这个漏洞的根源更加微妙涉及Servlet规范、容器实现和Shiro匹配逻辑的三方博弈。分号在URL中的角色在URL中分号;传统上被用作“参数”的分隔符这些参数被称为“路径参数”Path Parameters或“矩阵参数”Matrix Parameters格式如/path;param1value1;param2value2/segment。它们与查询字符串?keyvalue不同是附着在路径片段Path Segment上的。Servlet规范规定容器在调用request.getServletPath()或request.getPathInfo()时应该去除路径参数部分。容器处理以Tomcat为例当它收到请求/secure/admin.jsp;xxx时它会将;xxx识别为路径参数并在获取Servlet路径时将其剥离。因此request.getServletPath()返回的值是/secure/admin.jsp。Shiro的匹配逻辑漏洞点在存在漏洞的Shiro版本中用于构建匹配路径的代码可能没有完全遵循Servlet规范的这个细节。它可能在获取路径后没有正确地处理或剔除分号及其后面的内容。或者在Ant风格路径匹配器的实现中分号;被当作了普通字符。匹配再次错位Shiro拿着可能包含;xxx的路径字符串例如/secure/admin.jsp;xxx去匹配配置的Ant模式/secure/**。Ant模式/secure/**期望匹配以/secure/开头的所有路径。字符串/secure/admin.jsp;xxx是否匹配/secure/**这取决于Ant匹配器的具体实现。在某些实现中*和**通配符可能不会将分号视为特殊分隔符因此/secure/admin.jsp;xxx这个整体字符串是匹配/secure/**的因为它确实以/secure/开头。但是关键点在于攻击者可能利用一个顺序匹配的配置陷阱。假设配置是[urls] /secure/*.jsp authc /secure/* authc /public/** anon /** anon如果Shiro用于匹配的路径是/secure/admin.jsp;xxx它可能不匹配/secure/*.jsp因为模式期望以.jsp结尾而这里是.jsp;xxx。它也可能因为分号的存在而不匹配/secure/**通常匹配一个路径段而admin.jsp;xxx可能被视作一个段但模式/secure/*期望/secure/后只有一个段而admin.jsp;xxx包含分号逻辑复杂。如果它没有匹配到任何/secure/开头的规则它就会继续向下匹配最终可能落到/** anon上导致绕过。请求路由当容器将请求路由给真正的Servlet或JSP时它使用的是剥离了路径参数的/secure/admin.jsp。因此受保护的资源被正确找到并返回再次完成绕过。核心要点这个漏洞的本质是路径参数处理的差异性。Shiro在安全决策时对路径中分号的处理方式与Servlet容器在路由请求时对分号的剥离行为存在不一致。安全框架“看到”的路径带分号可能匹配了错误或宽松的规则和应用程序“执行”的路径不带分号是受保护资源再次不同。4.3 修复方案与最佳实践Shiro官方在1.5.2版本中修复了此漏洞。修复的核心思路确保Shiro在进行路径匹配时使用的路径与Servlet容器路由请求时使用的路径完全一致即必须剔除路径参数分号及之后的内容。具体技术实现修复主要发生在PathMatchingFilter或相关的路径解析工具类如WebUtils中。代码会显式地检查获取到的路径并移除第一个分号;及其之后的所有字符然后再用于Ant模式匹配。这模拟了Servlet容器的标准行为。例如在修复后的代码中可能会看到这样的逻辑String requestUri WebUtils.getPathWithinApplication(request); // 修复移除路径参数 int semicolonIndex requestUri.indexOf(;); if (semicolonIndex ! -1) { requestUri requestUri.substring(0, semicolonIndex); } // 然后使用清理后的requestUri进行路径匹配给开发者和运维的启示及时升级这是最直接有效的方法确保使用的Shiro版本 1.5.2。审查安全配置再次强调规则的顺序。采用“白名单”“默认拒绝”策略。优先放行明确的公开资源最后用严格的规则保护所有其他资源。WAF/网关层防护在应用前端部署Web应用防火墙WAF或API网关可以配置规则拦截或规范化包含可疑序列如/./、;的请求。但这只是纵深防御的一环不能替代代码修复。自定义过滤器如果因故无法立即升级可以考虑编写一个自定义的Servlet过滤器放置在Shiro过滤器之前对传入的HttpServletRequest进行路径清洗主动移除或规范化.和;为下游的Shiro提供一个“干净”的路径。但这种方法需要谨慎测试避免影响正常业务。5. 漏洞的深层共性与防御体系构建分析完两个具体的CVE我们可以跳出细节总结一些更高层次的规律和防御思路。5.1 漏洞的共通模式无论是“/./”还是“”它们都利用了同一种攻击模式“语义不一致攻击”。输入多样性攻击者提供一个“畸形”或“非标准”的输入包含特殊字符的URL。组件解析差异请求流经的系统组件负载均衡器、WAF、Servlet容器、Web框架、安全框架、业务代码对这个输入的解析方式存在细微差别。安全决策与执行分离某个组件这里是Shiro基于它解析后的视图做出了安全决策允许访问而另一个组件Servlet容器基于它解析后的视图执行了实际操作访问受保护资源。由于视图不同导致了权限绕过。5.2 构建多维度的防御体系修复一个CVE是治标建立稳固的防御体系才是治本。框架层加固治本之策持续更新密切关注所使用的安全框架、Web框架、容器和依赖库的安全公告及时打补丁。安全配置采用最小权限原则和默认拒绝策略配置Shiro规则。仔细检查规则的顺序避免宽松规则意外覆盖严格规则。自定义匹配逻辑对于关键路径可以考虑放弃Ant风格匹配使用精确路径匹配或正则表达式匹配需注意性能和安全减少模糊匹配带来的不确定性。代码层防御深度防御输入净化在业务逻辑开始处理请求之前对URL路径、参数进行严格的验证和规范化。使用标准库函数进行规范化确保整个应用层对路径的理解是一致的。权限校验前置不要完全依赖Web框架层的安全控制。在关键的业务方法、Service层甚至数据访问层可以再次进行权限断言例如使用Spring Security的方法级注解PreAuthorize或自定义AOP拦截实现多层校验。架构与运维层纵深防御WAF部署配置合理的WAF规则虽然不能防住所有逻辑漏洞但可以拦截大量已知攻击模式的扫描和自动化攻击。API网关在微服务架构中API网关可以作为统一的安全入口进行身份认证、流量清洗和路由转发将一些畸形请求拦截在业务系统之外。安全测试与代码审计将安全性测试纳入CI/CD流程。定期进行代码审计特别关注路径处理、URL跳转、权限校验等相关代码。使用SAST静态应用安全测试工具辅助发现潜在问题。监控与告警建立异常访问监控例如对频繁访问敏感路径、使用异常User-Agent或含有特殊字符的请求进行日志记录和告警。5.3 实战排查技巧与心得在实际工作中如果怀疑系统存在类似的路径处理漏洞可以尝试以下排查思路差异测试法对于同一个资源分别用正常路径和疑似绕过路径添加/./,;,//,..等进行访问观察响应是否一致。如果一致则可能存在绕过。日志分析法在Shiro和业务代码中增加详细日志打印出用于匹配的requestURI、servletPath、pathInfo以及最终匹配到的安全规则。对比正常请求和恶意请求下这些日志的差异是定位问题的最直接方法。调试跟踪法在开发或测试环境通过调试器跟踪org.apache.shiro.web.filter.PathMatchingFilter#getPathWithinApplication和org.apache.shiro.util.AntPathMatcher#doMatch等关键方法的执行过程观察路径字符串的变化和匹配结果。配置检查清单检查shiro.ini或Java配置中[urls]部分的规则顺序。检查是否有过于宽泛的/**规则被过早匹配。检查是否使用了authcBasicHTTP基本认证等不适用于Web浏览器的认证方式它们可能更容易受到这类绕过的影响。6. 从漏洞分析到安全开发思维的转变回顾CVE-2010-3863和CVE-2020-1957它们不仅仅是两个需要打补丁的漏洞编号。它们像两面镜子映照出我们在软件开发和安全设计过程中容易忽视的角落。首先是对“标准化”和“一致性”的敬畏。Web开发建立在如Servlet规范、HTTP RFC等一系列标准之上。漏洞往往源于对标准理解的偏差或实现的不完整。作为开发者在处理像URL、头信息、编码这些基础元素时必须严格遵循标准并使用经过广泛验证的库函数而不是自己臆造解析逻辑。其次是树立“不信任原则”。安全的第一要义是永远不要信任用户输入。这包括URL路径、查询参数、请求头、Cookie等一切来自客户端的数据。Shiro的这两个漏洞正是攻击者通过精心构造的“非标准但可能被某些组件接受”的路径输入钻了系统组件间信任传递的空子。任何来自外部的数据在参与安全决策、数据库查询、文件操作、系统命令执行之前都必须经过严格的验证、净化和规范化。最后是培养“攻击者思维”。在设计和评审系统时多问自己几个问题如果我是攻击者我会从哪里下手各个组件对接的边界是否清晰对同一份数据的理解是否绝对一致是否有默认放行的“后门”这种思维转换能帮助我们在漏洞被利用之前就发现潜在的风险点。安全是一个持续的过程而非一劳永逸的状态。通过对这些经典漏洞的深入剖析我们收获的不仅是修复特定问题的方案更是一套如何思考系统安全、如何构建防御纵深的方法论。下次当你编写或审查一段处理用户请求的代码时不妨想想这个“/./”和“”它们提醒我们魔鬼往往藏在最基础的细节之中。