사이트 간 스크립팅(또는 크로스 사이트 스크립팅, 영문 명칭 cross-site scripting, 영문 약어 XSS)은 웹 애플리케이션에서 많이 나타나는 취약점의 하나로 웹사이트 관리자가 아닌 이가 웹 페이지에 악성 스크립트를 삽입할 수 있는 취약점이다.
Wikipedia
삽입된 스크립트를 통해 다른 사이트와 정보를 교환하는 식으로 작동하기 때문에 Cross-Site
라는 이름이 붙었습니다..
XSS의 모든 경우에 대해서 직접 filter를 설정하는 것은 자원이 많이 소모되며 보안상 완벽할 수 없기 때문에 네이버의 오픈 소스인 lucy-xss-servlet-filter
라이브러리를 사용하기로 했습니다.
그런데 form-data
형식으로 오는 요청은 lucy-xss-servlet-filter
로 처리 가능하지만 JSON 형태로 오는 요청은 filtering이 되지 않아 messageConverter
로 변환하여 filter에 요청하는 방식으로 해결했습니다.
최신 버전 스프링 부트(2.6.9), lucy 필터(2.0.1) 기준 설정 방법은 다음과 같습니다.
웹에 찾아보면 대부분 Spring boot 이전 설정 파일인 Web.xml
, 빌드 도구로 Gradle
이 아닌 Maven
을 사용하는 예시가 많은데, 최신(2022.07 기준)설정 방법을 알아보겠습니다.
implementation 'com.navercorp.lucy:lucy-xss-servlet:2.0.1'
public class HTMLCharacterEscapes extends CharacterEscapes {
private final int[] asciiEscapes;
public HTMLCharacterEscapes() {
asciiEscapes = CharacterEscapes.standardAsciiEscapesForJSON();
asciiEscapes['<'] = CharacterEscapes.ESCAPE_CUSTOM;
asciiEscapes['>'] = CharacterEscapes.ESCAPE_CUSTOM;
asciiEscapes['&'] = CharacterEscapes.ESCAPE_CUSTOM;
asciiEscapes['\"'] = CharacterEscapes.ESCAPE_CUSTOM;
asciiEscapes['('] = CharacterEscapes.ESCAPE_CUSTOM;
asciiEscapes[')'] = CharacterEscapes.ESCAPE_CUSTOM;
asciiEscapes['#'] = CharacterEscapes.ESCAPE_CUSTOM;
asciiEscapes['\''] = CharacterEscapes.ESCAPE_CUSTOM;
}
@Override
public int[] getEscapeCodesForAscii() {
return asciiEscapes;
}
@Override
public SerializableString getEscapeSequence(int ch) {
return new SerializedString(StringEscapeUtils.escapeHtml4(Character.toString((char)ch)));
}
}
XssEscapeServletFilter
를 필터 Bean으로 등록@Configuration
@Slf4j
@AllArgsConstructor
public class XssConfig implements WebMvcConfigurer {
private final ObjectMapper objectMapper;
@Bean
public FilterRegistrationBean<XssEscapeServletFilter> filterRegistrationBean() {
FilterRegistrationBean<XssEscapeServletFilter> filterRegistration = new FilterRegistrationBean<>();
filterRegistration.setFilter(new XssEscapeServletFilter());
filterRegistration.setOrder(1);
filterRegistration.addUrlPatterns("/*");
return filterRegistration;
}
}
MappingJackson2HttpMessageConverter를 Bean으로 등록하여 수행합니다.
@Bean
public MappingJackson2HttpMessageConverter jsonEscapeConverter() {
ObjectMapper copy = objectMapper.copy();
copy.getFactory().setCharacterEscapes(new HTMLCharacterEscapes());
return new MappingJackson2HttpMessageConverter(copy);
}
{
"reviewTitle": "title",
"questions": [
{
"questionValue": "<script>alert(\"xss\")</script>"
},
{
"questionValue": "question2"
}
]
}
{
"reviewTitle": "title",
"updatedAt": 1658830928868,
"questions": [
{
"questionId": 1,
"questionValue": "<script>alert("xss")</script>"
},
{
"questionId": 2,
"questionValue": "question2"
}
]
}
하지만 사실 Spring Security를 쓰면 정말 간단하게 해결이 가능하다고 합니다😱.
@Configuration
public class SecurityConf extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.headers()
.xssProtection()
.and()
.contentSecurityPolicy("script-src 'self'");
}
}
Prevent Cross-Site Scripting (XSS) in a Spring Application
Spring Boot에서 JSON API에 XSS Filter 적용하기
Spring boot에 lucy-xss-servlet-filter 적용하기
[Spring/스프링] springboot gradle - XSS 스크립트 오류 해결하기 (with @RequestBody)
이거 폴더 구성좀 볼수있나요?