서버 스펙
- SpringBoot 2.3.4
- jdk 1.8
- 내장 톰캣 사용
- maven - war
이슈

위와 같은 정상 요청에 아래와 같이 요청 파라미터 내 처리 불가 데이터 삽입 후 요청하면 톰캣 디폴트 에러 페이지로 노출된다.
커스텀 에러페이지가 있지만, 해당 에러의 경우 요청이 서블릿/스프링 컨트롤러까지 도달하지 못하기 때문에 에러페이지로 보낼 수 없다.
공격구문
?keyword=""


HTTP 상태 400 – 잘못된 요청
요청 타겟에서 유효하지 않은 문자가 발견되었습니다. 유효한 문자들은 RFC 7230과 RFC 3986에 정의되어 있습니다.
클라이언트 오류로서 인지된 어떤 문제로 인하여, 서버가 해당 요청을 처리할 수 없거나, 처리하지 않을 것입니다. (예: 잘못된 요청 문법, 유효하지 않은 요청 메시지 framing, 또는 신뢰할 수 없는 요청 라우팅).
java.lang.IllegalArgumentException: 요청 타겟에서 유효하지 않은 문자가 발견되었습니다. 유효한 문자들은 RFC 7230과 RFC 3986에 정의되어 있습니다.
org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:509)
org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:511)
org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:831)
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1650)
org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
java.lang.Thread.run(Thread.java:748)
톰캣 기본 에러페이지 하단에 노출되는 톰캣 서버 정보는 보안상 취약점으로 분류된다.
문제점
- 공격자에게 불필요 정보 제공
- 서버 버전 노출은 공격자에게 "내가 쓰는 무기 모델명"을 친절하게 알려주는 꼴
- 서버 버전을 알면 공격자가 그 버전에 존재하는 알려진 취약점(CVE)을 검색하여 공격 시도 가능
- 톰캣 서버 버전에 대한 exploit 코드 확인 가능
- 서버 버전뿐만 아니라 운영체제, 프레임워크, DB 버전 등이 노출되는 것 자체가 위험
- 표적 공격 위험
- 최신 버전이 아닌 경우, 패치되지 않은 보안 이슈 검색하여 공격 시도 가능
- 최신 버전이라도, 해당 버전의 동작 특성을 기반으로 맞춤 공격 설계 가능
1. 원인
해당 에러는 톰캣 HTTP 커넥터 단계에서 이미 막힌 상태라,
요청이 서블릿/스프링 컨트롤러까지 도달하지 못하여 filter.java 소스코드를 추가하는 등의 서버 작업이 불필요하다.
커스텀 에러 페이지는 톰캣이 서블릿까지 요청을 전달한 뒤 처리하는 일반적인 HTTP 상태 코드에서만 동작하며,
HTTP 요청 라인 자체가 잘못된 경우에는 적용되지 않기 때문이다.
특수문자 " " 같은 RFC 위반 문자는 톰캣 커넥터에서 거부되므로 필터로 처리할 수 없다.
2. 해결
server.xml의 ErrorReportValve 설정을 추가한다.
Tomcat의 ErrorReportValve를 Host 레벨에 등록
- 톰캣 내부(파서 단계 포함)에서 발생한 HTTP 오류(400, 500 에러 등)를 처리할 때 기본 HTML 에러 페이지 내 노출되는 정보를 커스텀할 수 있음
- showReport="false", showServerInfo="false"는 톰캣 디폴트 에러 페이지 내 포함되는 내부 정보(각각 스택트레이스, 톰캣 버전)를 숨기는 설정 가능
server.xml
<Valve className="org.apache.catalina.valves.ErrorReportValve"
showReport="false"
showServerInfo="false" />
<Valve>를 넣을 수 있는 위치는 <Host> 안이다.
예를 들어 아래와 같이 <Host> 안에 위치하는 것으로 구성하면 된다.
<Host name="example.com"
xmlNamespaceAware="false"
xmlValidation="false"
autoDeploy="true"
appBase="webapps"
unpackWARs="true">
<!-- ErrorReportValve: Tomcat 내부(파서 단계 포함)에서 발생한 에러를
지정된 경로로 포워드/처리하도록 함 -->
<Valve className="org.apache.catalina.valves.ErrorReportValve"
showReport="false"
showServerInfo="false" />
<!-- 애플리케이션 컨텍스트 예시 -->
<Context reloadable="true" path="" docBase="your-app-directory">
<CookieProcessor sameSiteCookies="none" />
</Context>
</Host>
GET /path?keyword="" 처럼 요청 라인 자체에 RFC 위반 문자가 들어오면
Tomcat의 Http11InputBuffer 단계에서 Request Line 파싱이 실패하고,
해당 단계는 Servlet 컨테이너 진입 전이기 때문에
- web.xml의 <error-page>
- server.xml의 <Valve ... errorCode.400="...">
위의 경우, 톰캣 기본 에러페이지 외 커스텀 에러페이지로 보내는 코드이나
둘 다 동작하지 않고 톰캣 내장 ErrorReportValve 기본 로직이 실행된다.
즉, 톰캣이 리퀘스트 자체를 버리기 때문에 내가 만든 /error.html로 포워드 할 수가 없다.
따라서 Host 레벨에서 ErrorReportValve로 서버 단에서 버전/스택트레이스 노출을 차단하는 방법으로 처리해야 한다.
Valve
- Tomcat의 요청/응답 파이프라인에 끼워 넣는 확장 포인트
- 필터와 유사하나, Tomcat 내부(엔진/호스트/컨텍스트) 레벨에서 동작
- <Valve>는 server.xml의 Host 또는 Context 등에 배치 가능하며, 호스트 단위/애플리케이션 단위로 동작 범위를 가짐
- ErrorReportValve는 그 중 에러 응답을 생성/포워딩하는 역할을 담당하는 내장 Valve임
ErrorReportValve
- showReport="false"
- 톰캣 기본 에러페이지 내 에러메시지 상세 노출
- 개발자 디버깅용으로는 좋으나, 공격자 입장에서는 서비스 내부 구조 힌트가 된다.
- 취약점 탐색에 단서 제공, 에러 메시지 기반 공격, OWASP Top 10 항목 내 정보 노출에 해당
- 개발서버에서는 true 해놔도 문제 없겠으나, 운영서버에서는 false로 두는게 좋다.
- showServerInfo="false"
- 톰캣 기본 에러페이지 내 서버 버전 정보 노출
- 개발 및 운영서버 모두 false 세팅 권장
3. 확인
따라서 server.xml에 ErrorReportValve 코드를 추가하고 재기동하여 확인 결과,
톰캣 기본 에러페이지 내 스택트레이스와 서버 버전 정보를 확인할 수 없었다.
정상 요청

공격구문 포함하여 요청 조작
[공격구문]
?keyword=""

톰캣 기본 에러페이지

톰캣 기본 에러페이지 내 스택트레이스 및 서버 버전 정보가 미노출 처리되었다.
'[Project] > 업무일지' 카테고리의 다른 글
| [AWS/CloudWatch/RDS] 가용메모리 및 CPU 사용률 지표 확인 (0) | 2025.10.23 |
|---|---|
| [에러 핸들링] 잘못된 메소드 호출에 대한 응답 내 서버 버전 미노출 처리 (0) | 2025.09.17 |
| [카카오 로그인 API] 테스트앱 설정 (2) | 2025.08.12 |
| [Oracle 12c] SQL Error [1502] [72000]: ORA-01502: 인덱스 또는 인덱스 분할영역은 사용할 수 없는 상태입니다 (0) | 2025.07.29 |
| [Ajax/Get요청] GET 400 (Bad Request) (2) | 2025.07.21 |