Spring Boot에서 발생하는 InvalidMimeTypeException: 잘못된 MIME 타입 "application/xhtml+xml" 문제 해결

문제 상황

어떤 링크를 요청할 때 특정 사용자 또는 브라우저 환경에서 다음과 같은 예외가 발생하며 서버 응답이 실패하는 현상이 나타났습니다.

org.springframework.http.InvalidMediaTypeException: Invalid mime type "application/xhtml+xml": Invalid token character '+' in token "xhtml+xml"
    at org.springframework.http.MediaType.parseMediaType(MediaType.java:534)
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
    ...

이 오류는 Accept 헤더에 포함된 MIME 타입 파싱 과정에서 비정상적인 문자가 감지되었을 때 스프링 프레임워크 내부에서 발생합니다. 특히, 클라이언트에서 전송한 Accept: application/xhtml+xml 값에서 특수 기호 (PLUS SIGN) 대신 +(일반 플러스)가 아닌 유니코드 기호가 사용된 경우 문제가 됩니다.

발생 환경

항목 사용 버전
Spring Boot 2.1.8.RELEASE

근본 원인 분석

예외 메시지를 통해 MediaType.parseMediaType() 메서드가 비정상적인 토큰을 발견했다는 점을 알 수 있습니다. 디버깅을 통해 추적해보면, 문제는 클라이언트에서 전달된 Accept 헤더 값이 필터 체인을 거치면서 변조되는 데 있습니다.

특히, 애플리케이션에 적용된 XssFilter 클래스가 HTTP 요청 헤더 값을 사전에 인코딩하거나 치환하는 로직을 포함하고 있었으며, 이때 아래와 같은 코드가 문제를 일으켰습니다.

public String getHeader(String name) {
    String value = super.getHeader(xssEncode(name));
    if (value != null) {
        value = xssEncode(value);
    }
    return value;
}

여기서 xssEncode() 함수가 입력값 내의 특수 문자를 치환하다 보니, 정상적인 + 문자가 유니코드 형태의 (U+FF0B, FULLWIDTH PLUS SIGN)로 변경되어 전달되었습니다. 이 문자는 ASCII 범위를 벗어나며, MediaType 파서가 이를 유효한 토큰으로 인식하지 못해 예외가 발생하게 됩니다.

해결 방안

해당 필터가 헤더 값을 무분별하게 인코딩하지 않도록 수정해야 합니다. 특히 Accept, Content-Type과 같은 시스템 중요 헤더는 원본 값을 유지해야 하며, XSS 필터링은 본문 또는 쿼리 파라미터에 국한되어야 합니다.

수정된 필터 코드 예시:

public String getHeader(String name) {
    // Accept 헤더는 인코딩 없이 그대로 반환
    if ("accept".equalsIgnoreCase(name)) {
        return super.getHeader(name);
    }
    // 다른 헤더에 대해서만 인코딩 적용
    String value = super.getHeader(name);
    if (value != null && shouldEncodeHeader(name)) {
        value = xssEncode(value);
    }
    return value;
}

private boolean shouldEncodeHeader(String headerName) {
    List<String> safeHeaders = Arrays.asList("accept", "content-type", "user-agent", "referer");
    return !safeHeaders.contains(headerName.toLowerCase());
}

또는 더 근본적으로, XSS 필터는 요청 본문과 파라미터만 처리하고, 헤더는 건드리지 않도록 설계하는 것이 안전합니다.

검증 결과

수정 후 동일한 요청을 재시도했을 때, Accept 헤더가 정상적으로 파싱되었고, 더 이상 InvalidMediaTypeException이 발생하지 않았습니다. 모든 클라이언트 환경에서 안정적인 응답이 확인되었습니다.

요약 및 교훈

  • HTTP 헤더는 시스템 동작에 직접 영향을 주므로, 필터에서 임의로 수정하면 안 됩니다.
  • 특히 Accept, Content-Type 등 미디어 타입 관련 헤더는 매우 민감합니다.
  • 유니코드 정규화 및 특수 문자 치환이 예상치 못한 위치에서 문제를 유발할 수 있으므로 주의가 필요합니다.
  • 외부 라이브러리나 커스텀 필터를 사용할 때는 해당 컴포넌트가 어떤 방식으로 데이터를 조작하는지 반드시 검토해야 합니다.
  • 디버깅은 의심되는 경로를 단계별로 추적함으로써 가장 빠르게 원인을 파악할 수 있습니다.

태그: Spring Boot XSS 필터 HTTP 헤더 MIME 타입 InvalidMediaTypeException

6월 5일 20:59에 게시됨