CASE STUDY
한눈에 보는 변화
2022년 COMS에 Lucy XSS Filter를 처음 적용하고 whitelist 누락 구간을 보완한 경험을 기반으로, 2024~2025년에는 방어 범위를 GET·JSON·Multipart까지 확장했습니다. 공통 Request 처리 구조를 바꾸자 Forward 파라미터, 파일 Part, 메뉴 링크가 서로 다르게 영향을 받아 요청 유형별 생명주기와 기존 업무 계약을 함께 재검증했습니다.
기존 XSS 필터가 일부 POST 요청 중심으로 적용되어 요청 방식에 따라 입력값 검증 수준이 달랐습니다. 적용 범위를 확대하자 Forward 파라미터 누락, 파일 업로드, 메뉴 렌더링과 다운로드 링크 등 정상 기능에 Side Effect가 발생했습니다.
직접 담당한 범위
- 기존 필터·Wrapper·Forward 요청 흐름 분석
- GET 필터링 확대와 요청 유형별 처리 구조 설계·구현
- 예외 URL·허용 태그 속성 정리, 본사·일본·유럽법인 적용과 검증
| 구분 | 변경 전 | 변경 후 | 확인된 변화 |
|---|---|---|---|
| 검증 범위 | 일부 POST 요청 중심으로 Lucy XSS Filter 적용 | GET·일반 POST·JSON·Multipart별 전용 처리 경로 구성 | 4개 요청 흐름의 입력 검증 범위 보완 |
| Forward 파라미터 | Wrapper 내부 Map만 조회해 Forward에서 추가된 필수 값 누락 | 원본 Request fallback과 동일한 필터링 규칙 적용 | 기존 Forward 호출 계약 유지 |
| Body·파일 처리 | JSON Body 재사용과 Multipart 일반 파라미터 검증 경계 불명확 | Body 캐시와 파일 Part·일반 Form Data 책임 분리 | Controller 재사용과 파일 업로드 흐름 보존 |
| 적용 범위 | 본사 중심의 요청 방어 구조 | 법인별 URL·외부 연계 차이를 검증해 일본·유럽 COMS로 확산 | 본사+2개 해외법인 운영 반영 |
EVIDENCE CHAIN
문제를 좁힌 과정
- 01 · 흐름 분리
HTTP Method만 보지 않고 요청 데이터의 위치부터 나눴습니다
GET·일반 POST는 Parameter Map, JSON은 Request Body, Multipart는 파일 Part와 Form Data를 사용합니다. Forward는 새 요청을 만들지 않고 기존 Request를 이어서 사용하므로, 같은 Filter를 통과해도 데이터 생명주기가 같지 않았습니다.
판단모든 요청을 하나의 Wrapper로 감싸기보다 Method와 Content-Type별 책임을 분리해야 했습니다.
- 02 · 장애 재현
Forward에서만 필수 파라미터가 사라지는 조건을 재현했습니다
JSP가 필수 값을 추가해 Controller로 Forward하는 경로를 추적했습니다. 최초 파라미터는 Wrapper 내부 Map에 있었지만, Forward에서 추가된 값은 Servlet Container가 원본 Request에 반영하고 있었습니다.
판단값이 전달되지 않은 것이 아니라 Override된 getParameter가 원본 Request의 새 값을 보지 못한 것이 원인이었습니다.
- 03 · 보안 경계
fallback 자체가 검증 우회 경로가 되지 않는지 확인했습니다
Wrapper에 값이 없을 때 상위 Request를 조회하도록 보완하되, 원본에서 찾은 값에도 같은 필터링 규칙을 다시 적용했습니다. getParameterValues·getParameterMap·getParameterNames도 서로 다른 결과를 내지 않는지 함께 확인했습니다.
판단기존 Forward 계약을 복구하면서도 원본 Request 조회를 XSS 우회 경로로 남기지 않아야 했습니다.
- 04 · Side Effect 분리
메뉴·다운로드와 파일 업로드 문제를 같은 원인으로 묶지 않았습니다
href 제한으로 DB 기반 메뉴와 다운로드 링크가 사라진 문제는 최소 예외 URL로 분리했습니다. Multipart는 파일 스트림을 유지하고 일반 Form Data만 검증했으며, JSON은 Body를 캐시해 이후 Controller가 다시 읽을 수 있게 했습니다.
판단예외·Body·파일은 서로 다른 책임 경계이므로 각각의 정상 동작 조건을 별도로 정의해야 했습니다.
- 05 · 확산 검증
본사에서 동작한 규칙을 법인 시스템에 그대로 가정하지 않았습니다
본사 검증 후 일본·유럽법인의 메뉴, 업로드, 다운로드, 외부 연계 URL 차이를 별도 회귀 범위로 확인했습니다. 공통 방어 구조는 유지하되 법인별 예외는 확인된 최소 범위만 반영했습니다.
판단공통 보안 정책의 확산은 동일 설정 복사가 아니라 법인별 정상 업무 계약의 재검증까지 포함해야 했습니다.
DECISION LOG
검토한 대안과 선택 근거
Forward를 Redirect로 전환
브라우저의 새 요청으로 URL·파라미터 전달·화면 흐름이 함께 바뀌어 기존 호출 계약의 회귀 범위가 커지기 때문입니다.
모든 요청에 단일 Wrapper 적용
Parameter Map·JSON Body·Multipart Part의 읽기 방식이 달라 Body 소진과 파일 누락 가능성을 만들기 때문입니다.
문제가 난 경로를 넓게 예외 처리
예외는 공통 Filter가 책임지지 않는 구간을 만들므로 메뉴·다운로드 등 근거가 확인된 최소 범위로 제한했습니다.
요청 유형별 Wrapper와 최소 예외
각 데이터 형식의 생명주기를 보존하면서 차단 규칙과 기존 업무 흐름을 함께 검증할 수 있었습니다.
IMPLEMENTATION
설계와 구현
GET·Forward 파라미터
- HttpServletRequestNormalWrapper에서 정제된 Parameter Map 제공
- Forward 이후 추가된 값은 원본 Request에서 fallback하되 동일한 필터 재적용
- getParameter 계열 메서드가 일관된 값을 반환하도록 전달 경로 보완
JSON Body
- BodyReaderWrapper로 Filter 이후에도 Controller가 Body를 다시 읽을 수 있게 캐시
- JSONObject·JSONArray의 중첩 값을 재귀적으로 탐색해 문자열 값 필터링
- 요청 검증과 이후 JSON 역직렬화의 책임 경계 유지
Multipart 업로드
- MultipartWrapper에서 파일 Part와 일반 Form Data를 분리
- 파일 스트림·메타데이터는 유지하고 일반 파라미터에만 XSS 규칙 적용
- 기존 Controller가 기대하는 Multipart 요청 타입과 파일 조회 흐름 보존
예외·법인 확산
- 메뉴 렌더링·가이드·결과 다운로드에 필요한 URL과 태그 속성의 영향도 확인
- 예외 URL은 근거가 확인된 최소 범위로 설정하고 별도 회귀 대상으로 관리
- 본사 방어 구조와 추가 차단 정책을 일본·유럽법인 COMS 환경에 맞춰 반영
VERIFICATION
테스트 매트릭스
| 검증 영역 | 검증 방법 | 완료 기준 |
|---|---|---|
| 차단 동작 | GET·POST·JSON·Multipart별 허용 입력과 공격 입력 실행 | 정상 값은 유지되고 차단 대상은 요청 유형과 관계없이 필터링 |
| Forward 계약 | Forward 전후 기존 값과 새로 추가된 필수 파라미터 비교 | Controller에서 필요한 값 유지 및 fallback 값도 동일 규칙으로 검증 |
| Body·파일 | 중첩 JSON과 일반 파라미터를 포함한 파일 업로드 수행 | Body 재사용·파일 조회 정상 및 문자열 입력 필터링 |
| 업무 회귀 | 조회·등록·수정·메뉴·가이드·결과 다운로드 시나리오 실행 | 보안 적용 전과 동일한 정상 업무 흐름 유지 |
| 해외법인 | 일본·유럽의 법인별 메뉴·업로드·외부 연계 경로 점검 | 공통 방어 정책과 법인별 필수 예외가 함께 정상 동작 |
OPERATION
운영 반영과 후속 안정화
Forward 필수 파라미터 누락
- 분리한 원인
- Wrapper 내부 Map과 원본 Request에 값이 저장되는 시점 차이
- 대응
- 원본 Request fallback과 재필터링으로 기존 호출 계약 복구
메뉴·다운로드 링크 제거
- 분리한 원인
- href 제한이 DB 기반 정상 링크에도 동일하게 적용
- 대응
- 사용 경로를 확인한 최소 예외 URL·속성만 분리하고 회귀 테스트
업로드 파라미터 미검증
- 분리한 원인
- Multipart의 파일 Part와 일반 Form Data를 기존 Filter가 분리하지 못함
- 대응
- 파일은 보존하고 일반 파라미터만 검증하는 전용 Wrapper 적용
- 요청 방식에 따라 달랐던 XSS 검증 범위를 일관된 구조로 개선
- 보안 적용 확대로 발생한 기존 기능 Side Effect 해결
- 본사에서 확인한 방어 구조를 일본·유럽법인 시스템에 확산 적용
TAKEAWAYS
이 사례에서 드러난 역량
- 공통 XSS Filter는 문자열 치환 기능이 아니라 Request 계약 전체를 바꾸는 인프라 코드로 다뤄야 합니다.
- Forward 문제는 값의 존재 여부보다 Wrapper와 원본 Request 중 어느 경로를 읽는지 추적해야 원인을 찾을 수 있습니다.
- 보안 예외는 안전하다는 선언이 아니라 공통 필터의 책임을 제거하는 결정이므로 최소 범위와 대체 검증이 필요합니다.
- 방어 범위를 넓힐수록 공격 입력 차단과 정상 업무 회귀를 하나의 완료 기준으로 관리해야 합니다.