세상에서 가장 쉽게 최신 Spring Boot에 Lucy XSS 필터 적용하기

sojukang·2022년 7월 27일
2

XSS(Cross-Site Scripting)란?

사이트 간 스크립팅(또는 크로스 사이트 스크립팅, 영문 명칭 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) 기준 설정 방법은 다음과 같습니다.

Lucy XSS servlet filter

설정 방법

웹에 찾아보면 대부분 Spring boot 이전 설정 파일인 Web.xml, 빌드 도구로 Gradle이 아닌 Maven을 사용하는 예시가 많은데, 최신(2022.07 기준)설정 방법을 알아보겠습니다.

1. 의존성 추가

	implementation 'com.navercorp.lucy:lucy-xss-servlet:2.0.1'

2. /resource 폴더 내에 "lucy-xss-servlet-filter-rule.xml" 파일을 생성

공식 repository 참조

3. Escape 문자 지정

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)));
    }
}

4. 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;
    }
}

5. JSON 요청을 위해 JSON -> HttpMessage로 변환

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": "&lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;"
        },
        {
            "questionId": 2,
            "questionValue": "question2"
        }
    ]
}

Spring Security

하지만 사실 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)

profile
기계공학과 개발어린이

1개의 댓글

comment-user-thumbnail
2022년 9월 27일

이거 폴더 구성좀 볼수있나요?

답글 달기