Path Matching

2025. 2. 27. 23:57Spring Framework/Web on Servlet Stack

Spring MVC는 요청을 적절한 핸들러(컨트롤러)로 라우팅하기 위해 경로(Path) 매칭을 수행합니다.
그러나, 경로 매칭 과정에서 Servlet API의 경로 처리 방식과 URL 디코딩 문제 등으로 인해 다양한 복잡성이 발생할 수 있습니다.
이를 해결하기 위해 Spring MVC는 여러 가지 전략을 제공합니다.

1. Servlet API가 제공하는 경로 정보

Servlet API는 요청 경로를 여러 부분으로 나누어 제공합니다.

경로 유형 설명 예제 (/myapp/api/users/123)
requestURI 전체 요청 경로 /myapp/api/users/123
contextPath 애플리케이션 컨텍스트 경로 (기본적으로 web.xml에서 설정) /myapp
servletPath 서블릿 매핑 경로 /api
pathInfo 추가적인 경로 정보 /users/123

📌 즉, Spring MVC는 요청을 핸들러에 매핑할 때 contextPathservletPath를 제외한 lookupPath를 계산해야 합니다.

2. Path Decoding 문제와 보안 이슈

경로를 비교하기 위해 requestURI에서 contextPath 및 servletPath를 제외한 lookupPath를 추출해야 합니다.
그러나 경로를 디코딩할 때 몇 가지 문제가 발생할 수 있습니다.

🚨 문제 1: 디코딩된 경로 비교 불가능

  • servletPathpathInfo는 디코딩된 상태에서 제공되지만, requestURI는 인코딩된 상태로 존재합니다.
  • 따라서 requestURI를 디코딩해야만 비교할 수 있습니다.
  • 하지만 이 과정에서 예약된 문자(예: /, ;)가 디코딩되어 경로 구조가 변경될 위험이 있습니다.

📌 예제:

  • 원본 요청: /api%2Fusers/123 (인코딩된 /api/users/123):일반적으로 url에서 / 의 용도는 Separator(구분 기호)입니다. 그러나 이 예제에서 api와users 사이에 url의 Separator / 가 아닌 문자 /를 사용할 때 /가 %2F로 인코딩됩니다.
  • 디코딩 후: /api/users/123
  • / 문자가 디코딩되면서 경로가 달라져 보안 문제가 발생할 수 있음

🚨 문제 2: Servlet 컨테이너마다 servletPath 처리 방식이 다름

  • 일부 서블릿 컨테이너는 servletPath를 정규화(Normalize)하여 제공하지만, 그렇지 않은 경우도 있음
  • 결과적으로 requestURI.startsWith(servletPath) 비교가 불가능한 경우가 있음

📌 결론:
servletPath를 직접 사용하는 것이 비추천되며, DispatcherServlet이 servletPath 없이 동작하는 방식이 더 바람직함.

3. DispatcherServlet이 "/" 매핑을 사용할 경우

만약 DispatcherServlet이 / 또는 /*로 매핑되어 있다면,
Spring MVC는 서블릿 4.0+ 환경에서 servletPath 및 pathInfo를 완전히 무시할 수 있습니다.

📌 즉, 서블릿 컨테이너가 4.0 이상이면, Spring MVC가 servletPath와 pathInfo를 신경 쓰지 않고 requestURI만으로 처리 가능!

서블릿 3.1 환경에서는 어떻게 해결?

  • UrlPathHelper를 사용하여 alwaysUseFullPath=true 설정하면 같은 효과를 얻을 수 있음
@Bean
public UrlPathHelper urlPathHelper() {
    UrlPathHelper helper = new UrlPathHelper();
    helper.setAlwaysUseFullPath(true);
    return helper;
}

 

4. Path Decoding의 보안 문제 해결

경로를 매칭할 때 디코딩된 경로를 비교해야 하지만, 디코딩 시 보안 문제가 발생할 수 있습니다.

🚨 해결 방법 1: Spring Security의 HTTP Firewall을 사용하여 예약된 문자 차단

  • Spring Security를 활용하면 / 등의 예약된 문자가 포함된 요청을 거부할 수 있음
@Bean
public HttpFirewall httpFirewall() {
    DefaultHttpFirewall firewall = new DefaultHttpFirewall();
    return firewall;
}

 

🚨 해결 방법 2: UrlPathHelper에서 URL 디코딩 비활성화

  • 디코딩된 경로 비교를 하지 않고, 인코딩된 상태 그대로 비교할 수도 있음
@Bean
public UrlPathHelper urlPathHelper() {
    UrlPathHelper helper = new UrlPathHelper();
    helper.setUrlDecode(false); // URL 디코딩 비활성화
    return helper;
}

📌 주의:
컨트롤러의 요청 매핑도 인코딩된 상태로 작성해야 할 수도 있음.

5. PathPatternParser 사용 (Spring MVC 5.3+)

Spring MVC 5.3부터는 기존의 AntPathMatcher 대신 PathPatternParser를 사용할 수 있습니다.
Spring MVC 6.0부터는 기본적으로 PathPatternParser가 활성화됩니다.

📌 기존 방식: AntPathMatcher

  • lookupPath를 디코딩하거나 컨트롤러 매핑을 인코딩해야 경로 비교 가능

📌 새로운 방식: PathPatternParser

  • 경로를 RequestPath 객체로 변환 후 비교
  • 경로를 한 개의 세그먼트(segment) 단위로 개별 디코딩하여 비교
  • 보안 이슈 없이 경로 구조 유지 가능

✅ PathPatternParser 사용 방법

@Bean
public WebMvcConfigurer configurer() {
    return new WebMvcConfigurer() {
        @Override
        public void configurePathMatch(PathMatchConfigurer configurer) {
            configurer.setPatternParser(new PathPatternParser());
        }
    };
}

📌 이점:

  • 보안 문제 해결: "/api%2Fusers/123"을 안전하게 매칭 가능
  • URL 매칭 정확성 증가: lookupPath 디코딩 불필요
  • 서블릿 매핑(prefix mapping) 지원: 서블릿 경로가 포함된 URL도 안전하게 처리 가능

🚀 Summary

1️⃣ Servlet API의 경로 처리 방식

  • requestURI, contextPath, servletPath, pathInfo로 나누어져 있음
  • Spring MVC는 lookupPath를 생성해야 함

2️⃣ 경로 비교 시 발생하는 문제점

  • servletPath가 컨테이너마다 다르게 정규화됨
  • requestURI를 디코딩할 때 보안 문제가 발생할 수 있음

3️⃣ 해결 방법

  • DispatcherServlet을 /로 매핑하면 servletPath를 사용하지 않도록 할 수 있음
  • Spring Security HTTP Firewall을 이용해 보안 강화
  • UrlPathHelper에서 URL 디코딩을 비활성화 가능 (urlDecode=false)

4️⃣ Spring MVC 5.3+의 PathPatternParser 사용

  • 기존의 AntPathMatcher보다 안전하고 정확한 경로 매칭
  • 경로 세그먼트 단위로 개별 디코딩하여 보안 문제 해결
  • Spring MVC 6.0부터 기본 활성화

📌 결론:
Spring MVC 6.0 이상에서는 PathPatternParser를 사용하는 것이 가장 안전하고 추천되는 방식입니다! 🚀

 

출처 : https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-servlet/handlermapping-path.html

'Spring Framework > Web on Servlet Stack' 카테고리의 다른 글

Exceptions  (0) 2025.02.28
Interception  (0) 2025.02.28
Processing  (0) 2025.02.27
Servlet Config  (0) 2025.02.27
Web MVC Config  (0) 2025.02.27