사이트 간 스크립팅(또는 크로스 사이트 스크립팅, 영문 명칭 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)
이거 폴더 구성좀 볼수있나요?