Backend Development 9 min read

Spring Boot %2e Path Normalization Trick and Authentication Bypass in Versions ≤2.3.0.RELEASE

The article explains how Spring Boot versions up to 2.3.0.RELEASE normalize request paths—including decoding %2e and handling directory traversal—which can be exploited to bypass authentication, and shows the code differences that cause this behavior in newer releases.

Architect's Tech Stack
Architect's Tech Stack
Architect's Tech Stack
Spring Boot %2e Path Normalization Trick and Authentication Bypass in Versions ≤2.3.0.RELEASE

This article demonstrates a subtle security issue in Spring Boot where the default alwaysUseFullPath setting (false in versions ≤2.3.0.RELEASE) causes the framework to use ServletPath for route matching, triggering path normalization that decodes %2e and processes directory‑traversal sequences, potentially allowing authentication bypass.

Example Maven parent configuration for a vulnerable version:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.0.RELEASE</version>
    <relativePath/>
</parent>

A simple controller with two endpoints:

@RestController
public class httpbinController {
    @RequestMapping(value = "no-auth", method = RequestMethod.GET)
    public String noAuth() { return "no-auth"; }

    @RequestMapping(value = "auth", method = RequestMethod.GET)
    public String auth() { return "auth"; }
}

An interceptor that blocks all routes except /no-auth :

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(handlerInterceptor())
                .addPathPatterns("/**");
    }
    @Bean
    public HandlerInterceptor handlerInterceptor() {
        return new PermissionInterceptor();
    }
}

@Component
public class PermissionInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String uri = request.getRequestURI().replaceAll("//", "/");
        if (uri.contains("..") || uri.contains("./")) return false;
        if (uri.startsWith("/no-auth")) return true;
        return false;
    }
}

Using curl to request /no-auth/%2e%2e/auth on a vulnerable version returns auth because the interceptor sees the normalized path /no-auth/%2e%2e/auth and the .. is resolved, bypassing the check. The same request on version 2.3.1.RELEASE results in a 404, as alwaysUseFullPath is true, the framework uses getPathWithinApplication which does not re‑normalize the path, leaving the .. intact.

The root cause is the change in WebMvcAutoConfigurationAdapter#configurePathMatch between the two releases: the older release keeps alwaysUseFullPath false, while the newer one sets it to true for efficiency. This change also aligns with the behavior that caused CVE‑2020‑17523 in Apache Shiro when combined with improper configuration.

To avoid the issue, developers can explicitly set spring.mvc.pathmatch.matching-strategy=ant_path_matcher or upgrade to a Spring Boot version where alwaysUseFullPath is true, or ensure custom interceptors correctly handle encoded traversal sequences.

JavaSpring BootsecurityInterceptorPath NormalizationAuthentication BypassalwaysUseFullPath
Architect's Tech Stack
Written by

Architect's Tech Stack

Java backend, microservices, distributed systems, containerized programming, and more.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.