네이버 Lucy XSS Filter를 사용한 강력한 XSS 공격 방어 솔루션
Spring Boot 애플리케이션에 즉시 적용 가능한 XSS 필터링 모듈입니다. GET, POST, PUT, DELETE, PATCH 등 모든 HTTP 메서드의 파라미터와 JSON body를 자동으로 필터링합니다.
✅ 모든 HTTP 메서드 지원
- GET, POST, PUT, DELETE, PATCH 등 모든 메서드에 XSS 필터링 적용
- URL 쿼리 파라미터, Form 데이터, JSON Request Body 모두 지원
✅ HTML 엔티티 변환
- 위험한 문자를 HTML 엔티티로 변환:
<→<,>→> - 데이터 손실 없이 안전하게 보존
✅ 스마트 필터링
- 파일 업로드(multipart/form-data)는 자동 제외
- 이중 치환 방지 로직 내장
✅ 검증된 라이브러리
- 네이버에서 개발한 Lucy XSS Filter 사용
- 프로덕션 환경에서 검증된 안정성
- Spring Boot 3.3.2+
- OpenJDK 21
- Gradle 8.5
- Lucy XSS Servlet Filter 2.0.1
다음 파일들을 당신의 Spring Boot 프로젝트에 복사하세요:
src/main/java/com/lg/common/xss/
├── config/
│ ├── FilterConfig.java # Filter 등록 설정
│ └── XssRequestBodyAdvice.java # JSON body 필터링
├── filter/
│ ├── XssEscapeUtil.java # HTML 엔티티 변환 유틸
│ ├── XssProtectionFilter.java # XSS 방어 Filter
│ └── XssRequestWrapper.java # Request Wrapper
└── src/main/resources/
└── lucy-xss-superset.xml # Lucy XSS 설정 파일
build.gradle에 Lucy XSS 라이브러리를 추가하세요:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'com.navercorp.lucy:lucy-xss-servlet:2.0.1'
}
// 컴파일러 옵션 추가 (매개변수 이름 보존)
tasks.withType(JavaCompile).configureEach {
options.compilerArgs << '-parameters'
}복사한 파일들의 패키지를 당신의 프로젝트에 맞게 수정하세요:
// 변경 전
package com.lg.common.xss.filter;
// 변경 후 (예시)
package com.yourcompany.yourproject.security.xss;Spring Boot 애플리케이션을 실행하면 자동으로 XSS 필터링이 적용됩니다.
./gradlew bootRun로그에서 다음 메시지를 확인하세요:
XSS Protection Filter initialized successfully
XSS Filter test: '<script>alert('xss')</script>' -> '<script>alert('xss')</script>'
lucy_filter/
├── src/main/java/com/lg/common/xss/
│ ├── XssFilterApplication.java # 메인 애플리케이션 (테스트용)
│ ├── config/
│ │ ├── FilterConfig.java # ✨ Filter 등록 설정
│ │ └── XssRequestBodyAdvice.java # ✨ JSON body 필터링
│ ├── filter/
│ │ ├── XssEscapeUtil.java # ✨ HTML 엔티티 변환 유틸
│ │ ├── XssProtectionFilter.java # ✨ XSS 방어 Filter
│ │ └── XssRequestWrapper.java # ✨ Request Wrapper
│ └── controller/
│ └── TestController.java # 테스트용 Controller
├── src/main/resources/
│ ├── application.properties # 애플리케이션 설정
│ └── lucy-xss-superset.xml # ✨ Lucy XSS Filter 설정
├── build.gradle # Gradle 빌드 파일
└── test-xss.ps1 # PowerShell 테스트 스크립트
✨ = 필수 파일 (다른 프로젝트에 복사해야 함)
HTTP Request
↓
XssProtectionFilter (Servlet Filter)
├─→ multipart/form-data? → Skip (파일 업로드)
├─→ Already filtered? → Skip (이중 치환 방지)
└─→ XssRequestWrapper
├─→ URL 쿼리 파라미터 필터링
└─→ Form 데이터 필터링
↓
XssRequestBodyAdvice (RequestBodyAdvice)
└─→ JSON Request Body 필터링
↓
Controller
↓
Response (필터링된 데이터)
| 컴포넌트 | 역할 | 필터링 대상 |
|---|---|---|
| XssProtectionFilter | Servlet Filter로 모든 요청을 가로챔 | - |
| XssRequestWrapper | Request Wrapper로 파라미터 필터링 | URL 쿼리, Form 데이터 |
| XssRequestBodyAdvice | RequestBodyAdvice로 JSON body 필터링 | JSON Request Body |
| XssEscapeUtil | HTML 엔티티 변환 유틸 | 모든 String 값 |
요청:
curl "http://localhost:8080/api/user?name=<script>alert('xss')</script>"필터링 결과:
{
"name": "<script>alert('xss')</script>"
}요청:
curl -X POST http://localhost:8080/api/user \
-H "Content-Type: application/json" \
-d '{"name":"<script>alert(\"xss\")</script>","email":"test@test.com"}'필터링 결과:
{
"name": "<script>alert("xss")</script>",
"email": "test@test.com"
}요청:
curl -X POST http://localhost:8080/api/user \
-d "name=<img src=x onerror=alert(1)>" \
-d "email=test@test.com"필터링 결과:
{
"name": "<img src=x onerror=alert(1)>",
"email": "test@test.com"
}FilterConfig.java를 수정하여 특정 URL을 필터링에서 제외할 수 있습니다:
@Bean
public FilterRegistrationBean<XssProtectionFilter> xssFilterRegistration() {
FilterRegistrationBean<XssProtectionFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(xssProtectionFilter);
registrationBean.addUrlPatterns("/*");
// 특정 URL 제외
registrationBean.addInitParameter("excludeUrls", "/api/public/*,/health");
registrationBean.setOrder(1);
registrationBean.setName("xssProtectionFilter");
return registrationBean;
}lucy-xss-superset.xml을 수정하여 안전한 HTML 태그를 허용할 수 있습니다:
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://www.nhncorp.com/lucy-xss"
extends="lucy-xss-default.xml">
<!-- 특정 태그 허용 -->
<elementRule>
<element name="p">
<attributes>
<attribute name="style"/>
<attribute name="class"/>
</attributes>
</element>
<element name="br"/>
<element name="strong"/>
<element name="em"/>
<element name="a">
<attributes>
<attribute name="href"/>
<attribute name="target"/>
</attributes>
</element>
</elementRule>
</config>application.properties에서 로깅 레벨을 조정할 수 있습니다:
# XSS 필터 로깅 (DEBUG로 설정 시 모든 필터링 내역 출력)
logging.level.com.lg.common.xss=INFO
# 필터링 전후 값을 확인하려면 DEBUG로 변경
logging.level.com.lg.common.xss=DEBUG프로젝트에 포함된 test-xss.ps1 스크립트로 모든 HTTP 메서드를 테스트할 수 있습니다:
.\test-xss.ps1# GET 테스트
curl.exe "http://localhost:8080/api/test?name=<script>alert('xss')</script>&message=Hello"
# POST JSON 테스트
curl.exe -X POST http://localhost:8080/api/test -H "Content-Type: application/json" -d "{\"name\":\"<script>alert('xss')</script>\",\"message\":\"Hello\"}"
# POST Form 테스트
curl.exe -X POST http://localhost:8080/api/test/form -d "name=<script>alert('xss')</script>" -d "message=Hello"
# PUT 테스트
curl.exe -X PUT http://localhost:8080/api/test/1 -H "Content-Type: application/json" -d "{\"title\":\"<img src=x onerror=alert(1)>\",\"content\":\"Test\"}"
# DELETE 테스트
curl.exe -X DELETE "http://localhost:8080/api/test/1?reason=<script>alert('xss')</script>"
# PATCH 테스트
curl.exe -X PATCH http://localhost:8080/api/test/1 -H "Content-Type: application/json" -d "{\"status\":\"<svg onload=alert(1)>\"}"./gradlew clean build./gradlew bootRun또는
java -jar build/libs/lucy_filter-0.0.1-SNAPSHOT.jar- 기본 포트:
http://localhost:8080 - 헬스 체크:
http://localhost:8080/api/test/health
XssRequestWrapper에서 XSS_FILTERED_ATTRIBUTE 속성을 사용하여 이미 필터링된 요청인지 확인합니다:
private static final String XSS_FILTERED_ATTRIBUTE = "XSS_FILTERED";
public XssRequestWrapper(HttpServletRequest request) {
super(request);
// 이중 치환 방지를 위한 플래그 설정
request.setAttribute(XSS_FILTERED_ATTRIBUTE, true);
}
public static boolean isAlreadyFiltered(HttpServletRequest request) {
return request.getAttribute(XSS_FILTERED_ATTRIBUTE) != null;
}Content-Type이 multipart/form-data인 경우 필터링을 건너뜁니다:
private boolean isMultipartRequest(HttpServletRequest request) {
String contentType = request.getContentType();
if (!StringUtils.hasText(contentType)) {
return false;
}
return contentType.toLowerCase().startsWith(MULTIPART_FORM_DATA);
}RequestBodyAdvice를 구현하여 JSON Request Body의 모든 String 필드를 필터링합니다:
@ControllerAdvice
public class XssRequestBodyAdvice extends RequestBodyAdviceAdapter {
@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage,
MethodParameter parameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) {
if (body instanceof Map) {
filterMap((Map<String, Object>) body);
} else {
filterObject(body);
}
return body;
}
}애플리케이션 실행 시 다음과 같은 로그를 확인할 수 있습니다:
2026-01-23 14:30:00 - XSS Protection Filter initialized successfully
2026-01-23 14:30:00 - XSS Filter test: '<script>alert('xss')</script>' -> '<script>alert('xss')</script>'
2026-01-23 14:30:15 - Applying XSS filter to GET request: /api/test
2026-01-23 14:30:15 - XSS filtered [param: name] '<script>alert('xss')</script>' -> '<script>alert('xss')</script>'
2026-01-23 14:30:20 - Applying XSS filter to POST request: /api/test
2026-01-23 14:30:20 - XSS filtered [JSON field: name] '<script>alert('xss')</script>' -> '<script>alert('xss')</script>'
2026-01-23 14:30:25 - Skipping XSS filter for multipart request: POST /api/test/upload
- URL 파라미터/Form 데이터: 매우 낮음 (< 1ms)
- JSON Body: 낮음 (1-5ms, 데이터 크기에 따라 다름)
- 대용량 요청: JSON body가 큰 경우 필터링 시간 증가 가능
- 특정 URL 제외: 성능이 중요한 API는 필터링에서 제외
- 로깅 레벨 조정: 프로덕션 환경에서는 INFO 레벨 사용
- 필터 순서:
FilterConfig에서setOrder(1)로 가장 먼저 실행되도록 설정
- 성능: 모든 요청에 필터링이 적용되므로 대용량 트래픽 환경에서는 성능 테스트 필요
- 허용 태그 설정: 비즈니스 요구사항에 맞게
lucy-xss-superset.xml수정 필요 - JSON 응답: 필터링된 데이터는 HTML 엔티티로 변환되어 반환됨
- 데이터베이스: 필터링된 데이터가 그대로 저장되므로, 저장 전 디코딩이 필요한 경우 별도 처리 필요
아니요. XSS 필터는 HTTP 요청 시점에만 작동합니다. 이미 데이터베이스에 저장된 데이터는 별도로 처리해야 합니다.
네, 가능합니다. 프론트엔드 프레임워크의 XSS 방어와 함께 사용하여 다층 방어를 구현할 수 있습니다.
Spring의 HtmlUtils.htmlUnescape() 또는 Apache Commons의 StringEscapeUtils.unescapeHtml4()를 사용하세요.
XssRequestBodyAdvice를 수정하여 특정 필드를 제외하거나, 어노테이션 기반 필터링을 구현할 수 있습니다.
네, Servlet Filter 기반이므로 Spring MVC, Thymeleaf 등 모든 Spring 웹 애플리케이션에서 작동합니다.
이 프로젝트는 오픈소스입니다. 버그 리포트, 기능 제안, Pull Request를 환영합니다.
이 프로젝트는 학습 및 참고용으로 제공됩니다. 자유롭게 사용하고 수정할 수 있습니다.
Made with ❤️ for secure Spring Boot applications