URL解析导致的鉴权绕过问题探究-SpringSecurity篇

上篇文章中分析了Resin解析URL的流程和解析过程中存在的绕过技巧。本文则主要分析Spring生态中的核心鉴权框架-Spring Security,研究了其鉴权解析流程、用于匹配鉴权的RequestMatcher实例类的逻辑、可能存在鉴权绕过的场景,复现分析了由于Spring Security与多个后端框架(SpringMVC、自写Servlet、SpringWebflux)解析不一致而导致的鉴权绕过漏洞。在分析过程中也发现了公网未提到的几个绕过场景, 如果对文章有疑问/建议或者想一起研究交流的师傅,欢迎私信~

1、Spring Security 解析过程

解析过程基于的是spring-security 5.6.3版本

真实情况是最开始从org.apache.catalina.core.ApplicationFilterChain#doFilter开始解析,这部分属于tomcat的解析逻辑,一直到org.springframework.security.web.FilterChainProxy#doFilter就属于了spring security的范畴。在当前类的doFilterInternal方法中,调用spring security的HttpFirewall接口进行恶意字符的校验

HttpFirewall接口有2个实现类:DefaultHttpFirewall、StrictHttpFirewall(严格模式)

spring security默认使用的就是StrictHttpFirewall严格模式的校验

依次来看下:

1、rejectForbiddenHttpMethod 校验请求方法,如果请求方法不在默认的这7个方法中,会报错RequestRejectedException

2、rejectedBlocklistedUrls 这个方法会对url内容进行检查。如果url中包含特殊字符,那么会报错不再执行后续逻辑

1
2
3
4
5
6
7
8
9
decode:request.getServletPath()没包含的话,就检查request.getPathInfo()    经过归一化处理的字符串
encode:request.getContextPath()没包含的话,就检查request.getRequestURI() 直接传递的字符串

如请求/hello/./../test2
request.getRequestURI() == /hello/./../test2
request.getContextPath() == ""

request.getPathInfo() == null
request.getServletPath() == test

主要限制了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1、编码列表:
分号; %3b %3B
反斜杠\ %5c %5C
双斜杠// %2f%2f %2f%2F %2F%2f %2F%2F
%25(%)
%2f %2F(/)
%2e %2E(.)
%00
空字符

2、未编码列表:
分号; %3b %3B
反斜杠\ %5c %5C
双斜杠// %2f%2f %2f%2F %2F%2f %2F%2F
百分号%
%2f %2F(/)
%00
空字符

对于请求路径/hello/./../test2来说,filterchain调用rejectedBlocklistedUrls时并不会报错,但是会在后面的isNormalized 方法中报错

/hello/%2e/test2 访问时

当然,如果系统本身业务确实需要传入限制字符,也可以调用方法去除限制:StrictHttpFirewall#setAllowSemicolon设置为true表示允许;出现、setAllowUrlEncodedPeriod设置%2e、setAllowUrlEncodedDoubleSlash设置//字符

3、rejectedUntrustedHosts 默认为true

4、isNormalized 对4个地址进行处理,如果返回false 那么请求就会报错:The request was rejected because the URL was not normalized.

1
2
3
4
request.getRequestURI()
request.getContextPath()
request.getServletPath()
request.getPathInfo()

如果包含/. /.. 字符,说明路径未规范化,那么会报错

5、containsOnlyPrintableAsciiCharacters 查看request.getRequestURI()是否包含非ascii字符

另外一个 DefaultHttpFirewall相对严格模式的StrictHttpFirewall 而言逻辑很简单,在getFirewalledRequest方法中:1、调用isNormalized 检查ServletPath跟PathInfo的归一化; 2、调用containsInvalidUrlEncodedSlash检查RequestURI是否包含%2f(/)

1
2
3
4
5
6
如请求/hello/./../test2
request.getRequestURI() == /hello/./../test2
request.getContextPath() == ""

request.getPathInfo() == null
request.getServletPath() == test

2、Spring Security RequestMatcher匹配及绕过

RequestMatcher接口的实现类用于匹配请求url是否符合系统定义的匹配规则,如果match方法返回true表示匹配 需要认证。

下文提到的鉴权绕过问题 总的来说就是Spring Security与后端框架对于url pattern的解析不一致导致RequestMatcher#match匹配不到请求路由 返回false,但是后端却能将路由解析到具体方法的情况

Spring Security历史上出现的绕过问题多是这样的情况,如CVE-2023-34034 是与Spring WebFlux组合时的绕过、CVE-2023-34035 是与自写Servlet组合时的绕过、CVE-2023-20873 是与Cloud Foundry组合时的绕过、CVE-2022-22978 RegexRequestMatcher、CVE-2023-20860 mvcRequestMatcher 是与spring mvc组合时正则.默认不匹配换行符、 无前缀的通配符无法正确匹配路由的问题。

如下是几个重点的RequestMatcher鉴权类及一些鉴权绕过的细节分析

2.1 AntPathRequestMatcher

AntPathRequestMatcher#matches

首先判断鉴权配置方法与用户传入的方法是否一致,如果不一致,返回false 放过鉴权;如果正则为/**,那么返回true;如果不是/**,那么调用AntPathRequestMatcher#getRequestPath获取url后进行matcher匹配。根据系统设置的过滤规则,matcher分为SpringAntMatcher、SubpathMatcher,在实例化AntPathRequestMatcher对象时就会指定matcher到底是哪一个,如果pattern满足条件:不为/**、不为**、以/** 结尾、且不包含 ? { } 这三种字符、倒数第二个位置的字符是*,则创建一个 SubpathMatcher 对象,否则指定为SpringAntMatcher对象

实例配置如下,spring security就会创建SubpathMatcher进行匹配

1
2
3
4
5
6
7
8
9
10
com.example.springsecurity.config.SecurityConfig

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests().antMatchers("/hello/**").authenticated();
}
}

SubpathMatcher#matches,区分大小写开关caseSensitive(默认为true),然后将用户传入的path与subpath进行比对,subpath是pattern.substring(0, pattern.length() - 3),即本次演示的/hello

如果path与subpath相同,或者以subpath开头、后面紧跟着/,那么会鉴权,返回403

如果配置是/hello/test2,那么使用SpringAntMatcher进行匹配判断

1
2
3
4
5
6
7
8
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests().antMatchers("/hello/test2").authenticated();
}
}

2.2 AntPathRequestMatcher 绕过

spring mvc trailingSlashMatch 属性绕过

当spring security对后端的spring mvc controller进行鉴权时,由于spring mvc的trailingSlashMatch配置属性默认为true,会认为/hello/test2/与/hello/test2 相同,都能定位到具体业务方法。但是spring security会认为这是两个url,所以当配置对/hello/test2鉴权时,用户可以使用/hello/test2/进行绕过

1
2
3
4
5
6
7
8
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests().antMatchers("/hello/test2").authenticated();
}
}

访问/hello/test2 需要鉴权

访问/hello/test2/ 绕过鉴权

这种绕过方式最早(不完全考证)由landgrey师傅报送官方,但是官方回应文档已建议使用MvcRequestMatcher替代AntPathRequestMatcher,没发布专门的安全公告

https://landgrey.me/blog/23/

https://docs.spring.io/spring-security/site/docs/5.5.0/reference/html5/#MvcRequestMatcher

spring mvc SuffixPatternMatch 后缀匹配模式绕过

当spring security对后端的spring mvc controller进行鉴权时,如果启用了SuffixPatternMatch 后缀匹配模式,/hello/test2与/hello/test2.do 的匹配结果是一样的。但是spring mvc在5.3及之后的版本将 RequestMappingHandlerMapping#useSuffixPatternMatch 默认值true改为了false,所以此绕过方式适用于spring mvc <5.3

https://github.com/spring-projects/spring-framework/issues/23915

在国产系统蓝*就出现过这个问题,该系统使用了低版本的spring mvc 5.0.19,useSuffixPatternMatch默认为true,支持后缀匹配。同时此系统在spring.xml进行权限配置时,判断gif、jpg、tmpl等结尾的路由是静态资源,默认不经过系统安全鉴权

这样利用SuffixPatternMatch 后缀匹配的特性,访问/xx/target.tmpl(不需要权限)达到与/xx/target(需要权限)相同的效果,从而绕过spring secutiry的鉴权

低版本关闭SuffixPatternMatch 的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1、调用setUseSuffixPatternMatch
@Configuration
@ComponentScan
@EnableWebMvc
public class AppConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(false);
}
}


2、配置文件中annotation-driven标签配置:
<mvc:annotation-driven>
<mvc:path-matching suffix-pattern="false" />
</mvc:annotation-driven>

2.3 RegexRequestMatcher

RegexRequestMatcher#matches方法根据正则模式进行匹配,如果是正常的请求方法,会获取servletpath 调用java.util.regex.Pattern#matcher进行正则表达式匹配

使用如下权限配置,/hello/下的路径都需要经过认证

1
2
3
4
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests().regexMatchers("/hello/.*").authenticated();
}

2.4 RegexRequestMatcher绕过

当使用RegexRequestMatcher配置校验固定路径、/.*、/*等时,可使用?字符进行绕过

1
2
3
4
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests().regexMatchers("/hello/test2").authenticated();
}

/hello/test2?

2.5 RegexRequestMatcher 绕过 CVE-2022-22978

RegexRequestMatcher#pattern默认的匹配模式不匹配\r \n换行符,所以当配置校验是/hello/.*时,可使用/hello/test2%0a绕过

修复的commit: https://github.com/spring-projects/spring-security/commit/70863952aeb9733499027714d38821db05654856

在默认情况下,正则表达式中的.不会匹配换行符。而修复后的Pattern.DOTALL模式是单行模式:更改了.的含义,使它与每个字符都匹配,包括换行符\r \n

常量值参考:https://docs.oracle.com/javase/8/docs/api/constant-values.html

2.6 MvcRequestMatcher

早期的spring security默认使用AntPathRequestMatcher匹配鉴权,AntPathRequestMatcher与spring mvc HandlerMappingIntrospector匹配路由的解析差异 导致鉴权/index被 /index/, /index.html 绕过。为此官方新增了MvcRequestMatcher类,改为与后端spring mvc一致的HandlerMappingIntrospector类匹配路径

鉴权匹配的起点是org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher#matches

该方法主要操作有两步:1是根据用户请求的url获取处理的HandlerMapping实例类;2是根据第一步获取的HandlerMapping#parser解析器解析规则pattern,再进行路由url与规则pattern的匹配工作

1、根据url获取对应的MatchableHandlerMapping实例类,这里获取的是PathPatternMatchableHandlerMapping

HandlerMappingIntrospector#doWithMatchingMapping依次遍历handlerMappings,调用其getHandler方法后将返回结果传递给PathSettingHandlerMapping并返回

handlerMappings有5个,依次遍历。第一个是RequestMappingHandlerMapping,不存在getHandler,所以会一直调用到爷爷类AbstractHandlerMethodMapping#getHandlerInternal,其调用的lookupHandlerMethod方法返回的HandlerMethod实例是具体处理该路由的hander方法(Controller中定义的方法)

而且doWithMatchingMapping方法这里是个lambda表达式,在方法中执行apply方法时会执行{}内的逻辑代码

1
2
3
return (MatchableHandlerMapping)this.doWithMatchingMapping(wrappedRequest, false, (matchedMapping, executionChain) -> {
//逻辑代码
}

最终根据获取到的mapping及requestPath路径组装HandlerMappingIntrospector.PathSettingHandlerMapping实例并返回,接着执行match方法

2、调用PathPatternMatchableHandlerMapping#match进行匹配,分为两步:

第一步调用PathPatternParser#parse解析系统配置的鉴权正则pattern,处理了/ ? { } : *这几个字符出现在pattern的情况,根据/将正则pattern拆分得到多个对应的PathElement实例集合,最终返回PathPattern实例

第二步调用PathPattern#matches开始真正的匹配工作:遍历上一步得到的PathElement实例节点,调用对应的pathElements实例match方法进行匹配

其他类型规则的PathElement实例

1
2
3
4
5
6
7
8
分离器元素  SeparatorPathElement(/)
通配符元素 WildcardPathElement(/api/*)
单字符通配符元素 SingleCharWildcardedPathElement(/api/?)
统配剩余路径元素 WildcardTheRestPathElement(/api/**)
将路径作为变量捕获的元素 CaptureVariablePathElement(/api/{param})
捕获路径其余部分的元素 CaptureTheRestPathElement(/api/{*param})
字面路径元素 LiteralPathElement(/api/index)
正则表达式元素 RegexPathElement(/api/.*)

第一步,如果权限匹配规则是**,最终返回的PathPattern实例

第二步接着调用PathPattern#matches开始真正的匹配工作:依次根据上一步获取的PathElement进行匹配

**对应的PathElement实例是RegexPathElement,所以会调用匹配到的是RegexPathElement#matches

2.7 MvcRequestMatcher 绕过 CVE-2023-20860

在23年3月份,Spring官方发布了一份cve-2023-20860安全通告,表示MvcRequestMatcher 在使用无前缀双通配符(**)的情况下,会存在Spring Security与Spring MVC的解析逻辑不一致导致的绕过

CVE-2023-20860官方公告链接:https://spring.io/security/cve-2023-20860

影响版本: Spring Framework 6.0.0 to 6.0.6、5.3.0 本地测试复现版本:5.3.24

开发者通常会认为**表示全路径,在使用Spring Security的mvcMatchers鉴权时,可能会使用如下配置规则

1
2
3
4
5
6
7
8
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests().mvcMatchers("**").authenticated();
}
}

控制器HelloController

1
2
3
4
5
6
7
8
9
10
11
@RestController
public class HelloController {
@GetMapping("/hello/test2")
public static String test2(){
return "test2 page";
}
@GetMapping("test3")
public static String test1(){
return "test3 page";
}
}

按照平常思维理解,**鉴权表示对任意路径进行鉴权。但是实际测试发现访问/hello/test2、/test3 都不需要进行鉴权,这里的鉴权是失效的

绕过的核心本质在于:MvcRequestMatcher对于pattern规则的解析与spring webmvc对于无前缀url的解析不一致导致的绕过问题

对于spring controller而言,如下两个的路由是等价的

1
2
@GetMapping("/hello/*")
@GetMapping("hello/*")

但是对于spring security的MvcRequestMatcher使用的PathPattern来说,并不等价。所以当正则不以/开头时,是匹配不到/hello/test2路由的

MvcRequestMatcher使用PathPattern对鉴权正则pattern与请求路由url两者是否匹配进行判断,而PathPattern根据不同的pattern会选择不同的PathElement实例处理

1
2
当匹配规则为/**时,对应的PathElement是WildcardTheRestPathElement
当匹配规则为**时,对应的PathElement是RegexPathElement

想到之前的RegexRequestMatcher存在换行符绕过的问题,RegexPathElement是否存在呢,查看最新版代码发现增加了Pattern.DOTALL,换行符无法利用了

修复:在新版本的HandlerMappingIntrospector#getMatchableHandlerMapping中将返回的PathSettingHandlerMapping替换为LookupPathMatchableHandlerMapping,这两个的区别是后者的match方法对于正则pattern不是/开头的 都在开头添加/,解决了**匹配不到路由的问题

HandlerMappingIntrospector.LookupPathMatchableHandlerMapping#match

仔细观察修复方案是在HandlerMappingIntrospector中修复的,那么spring security与其他框架结合时的匹配是不是也可能存在问题?tkswifty师傅排查到了spring security与webflux结合的时候也存在类似于CVE-2023-20860的问题:CVE-2023-34034,复现详情见:https://forum.butian.net/share/2373

2.8 requestMatchers 绕过 CVE-2023-34035

除了spring mvc、spring webflux,还有可能集成自写的servlet,spring security与自写servlet的解析也存在着解析差异,官方分配漏洞编号:CVE-2023-34035

发现者是在测试 GraphQL 项目与 Spring Security集成时, Spring Security得鉴权不生效 从而报告给了官方。官方判定是由于 GraphQL 依赖项将一个额外的 servlet 注册到 servlet 上下文中 。导致与 requestMatchers(String) 匹配规则冲突导致的鉴权失效问题,发现者记录原文:https://deepkondah.medium.com/how-i-discovered-cve-2023-34035-improper-authorization-f597812f2fac

漏洞复现:总体项目代码有三个类:鉴权类、Controller控制器类、自写的Servlet类,另外还需要在springboot启动类添加@ServletComponentScan注解,具体代码如下

鉴权SecurityConfigHigh类代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.example.springsecurity.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfigHigh {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests().requestMatchers("/admin/**").hasRole("ADMIN").requestMatchers("/manage/**").hasRole("MANAGE").anyRequest().permitAll();
return http.build();
}

}

Controller控制器类代码

1
2
3
4
5
6
7
8
9
10
11
12
package com.example.springsecurity.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class ManageController {
@GetMapping("/manage/page")
public String Manage(){
return "manage";
}
}

自写的Servlet类代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.example.springsecurity.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(urlPatterns = "/admin/*")
public class UserServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

@Override
public void init() throws ServletException {
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().write("user page");
return ;
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
this.doGet(req, resp);
}
@Override
public void destroy() {
}
}

鉴权配置含义是对除/manage/**/admin/**外的路由放行,对/admin/**进行ADMIN角色权限判断,对/manage/**进行MANAGE角色权限判断。但是由于 Spring Security 的requestMatchers("/admin/**")规则不能正常匹配用户自写的Servlet url导致的鉴权功能失效

访问/manage/page,返回403无权限,鉴权功能正常

访问/admin/page,可以看到成功请求到UserServlet#doGet方法并返回方法执行结果,鉴权功能失效

修复:使用AntPathRequestMatcher是安全的,而使用MvcRequestMatcher存在匹配不到的问题。所以spring security改变了AbstractRequestMatcherRegistry创建RequestMatcher实例的逻辑

总体来说是:5.8.4及之前的spring security在存在spring controller的情况下就会直接创建MvcRequestMatcher,而新版本并未对MvcRequestMatcher代码做改变,而是改变了创建MvcRequestMatcher的条件:1、存在spring controller;2、context继承WebApplicationContext、且存在servletContext;3、存在DispatcherServlet且只有一个Servlet。只有满足了这几个条件,才会调用createMvcMatchers创建MvcRequestMatcher

这样一来,我们上面复现的例子存在除DispatcherServlet另外的Servlet,所以创建的是AntPathRequestMatcher,修复了鉴权绕过的问题。但其实如果系统显式指定自写Servlet使用MvcRequestMatcher,依旧会存在匹配不到导致鉴权绕过的问题。但是官方也针对这个情况做了处理,如下图是手动指定MvcRequestMatcher匹配自写Servlet时,spring项目启动报错提示

1
http.authorizeHttpRequests().requestMatchers(String.valueOf(new MvcRequestMatcher.Builder(new HandlerMappingIntrospector()).servletPath("/admin/**"))).hasRole("MANAGE").anyRequest().permitAll();

尝试利用寻找能注册servlet,但是spring无法完全统计识别servlet数量的思路,绕过如上的检测。但是测试了4种注册servlet的方式,spring security均能检测到,都无法强制使用MvcRequestMatcher进行强制匹配

1
2
3
4
1、WebServlet注解方式
2、spring提供的servlet注册接口ServletRegistrationBean#setServlet方式
3、servletContext#addServlet添加的方式
4、将Serlvet当做普通的bean注册给Spring的方式(需要注册多个servlet)

最后一种注册的方式比较奇特,开发者不需要指定路由,路由url是servlet的名字前缀,且末尾需要添加/才能正常访问。所以在使用ant进行匹配时,单纯匹配/admin是无效的,需要匹配/admin/

3、挖掘鉴权绕过场景

根据我们上面漏洞分析的经验来看,想要挖掘一个新的绕过场景,可以从两方面进行发力:1、找各种后端解析的pattern,挖掘ant、regex、mvc等解析器与后端对同一个pattern理解不一致的情况;2、基于历史漏洞找,找同一个利用方式在不同框架的应用,例如CVE-2023-34034与CVE-2023-20860的关系

对于第一种方式,我注意到了在servlet中/admin/*的pattern,可以匹配/admin,但是在spring security理解中是无法匹配/admin,可能会存在潜在的安全绕过

在CVE-2023-34035的修复方案中,官方建议后端是spring mvc endpoints 只能使用MvcRequestMatcher ,其他的endpoints都使用AntPathRequestMatcher,所以我在尝试将AntPathRequestMatcher与自写的servlet结合时,发现了这个问题

自写的servlet 有三种匹配模式:1精确匹配 /admin/index;2路径匹配 /admin/*;3后缀匹配 *.do 精确匹配已经被分配了CVE-2023-34035,后面两种匹配模式都存在绕过的场景

漏洞复现,使用spring security V5.8系列的最新版5.8.7

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
//SecurityConfigHigh.java
package com.example.springsecurity.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;

@Configuration
@EnableWebSecurity
public class SecurityConfigHigh {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests().requestMatchers(new AntPathRequestMatcher("/admin/*")).hasRole("MANAGE").anyRequest().permitAll();;
return http.build();
}
}



//HelloController.java
package com.example.springsecurity.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
@GetMapping("/hello/test2")
public static String test2(){
return "test2 page";
}
}



//UserServlet.java
package com.example.springsecurity.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(urlPatterns = "/admin/*")
public class UserServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

@Override
public void init() throws ServletException {
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().write("user page");
return ;
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
this.doGet(req, resp);
}
@Override
public void destroy() {
}

}

当访问/admin/mawkdemawk路径时,spring security起了作用,返回403无权限

而当访问/admin时,spring security的AntPathRequestMatcher对匹配/admin失效,但servlet依然能够请求到UserServlet#doGet方法

另外一个问题就是上文提到的RegexRequestMatcher关于?处理的问题

这个问题在最新版依然存在

1
2
3
4
/admin 会鉴权导致无法访问  但是可以通过/admin? 进行绕过
.requestMatchers(new RegexRequestMatcher("/admin","GET")).hasRole("MANAGE").anyRequest().permitAll();
.requestMatchers(new RegexRequestMatcher("/admin/","GET")).hasRole("MANAGE").anyRequest().permitAll();
.requestMatchers(new RegexRequestMatcher("/admin/*","GET")).hasRole("MANAGE").anyRequest().permitAll();

希望对正在研究此类问题的师傅有帮助~


URL解析导致的鉴权绕过问题探究-SpringSecurity篇
https://pwnull.github.io/2023/from-urlparser-to-authbypass-SpringSecurity/
作者
pwnull
发布于
2023年9月28日
许可协议