Spring/Spring Boot

[Spring/스프링] springboot gradle - XSS 스크립트 오류 해결하기 (with @RequestBody)

민돌v 2022. 4. 5. 18:05
728x90

프로젝트 댓글에, 누군가 XSS 공격을 해놓았다.. 껄껄껄

이론으로만 알던걸 직접 당해보니 기분이 새롭고 상쾌하다

해결하자!!

 


1.XSS 란

2. Spring에서 Xss 해결하기


1. XSS 란

Crooss-site Scripting으로, 클라이언트가 입력값으로 악의적인 스크립트 문을 삽입하여, 개발자의 의도대로 동작하지 않게하는 공격을 말합니다.

 

바로 아래처럼,,..!! ㅠㅠ

껄걸껄

 

 


2. Spring에서 Xss 해결하기

XSS 공격을 예방하는 방법으로는, 

  1. 특정문자 치환하기
  2. 특정문자 입력시 검사 후, 입력받지 않게하기
  3. 모든 body태그에 C:out 입력하기.. 등등

 

하지만! 네이버에서 이 기능등을, lucy xss servlet filter라는 정의해둔 라이브러리가 존재한다, 이걸 프로젝트에 적용해서 사용해보자

 

나는 Spring boot Gradle 프로젝트에 Lucy Xss servlet filter를 적용해보았다.

 

1) 라이브러리 설치

먼저 라이브러리를 설치해보자

 

build.gradle

//xss예방 - 네이버 filter
implementation 'com.navercorp.lucy:lucy-xss-servlet:2.0.0'
implementation 'com.navercorp.lucy:lucy-xss:1.6.3'
//StringEscapeUtils 를 사용하기 위해 commons-text 의존성 추가
implementation 'org.apache.commons:commons-text:1.8'

 

2) 그 다음, src/main/resource 폴더 바로 아래 xml 파일을 만들어줘야한다.

lucy-xss-servlet-filter-rule.xml

<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://www.navercorp.com/lucy-xss-servlet">
   <defenders>
       <!-- XssPreventer 등록 -->
       <defender>
           <name>xssPreventerDefender</name>
           <class>com.navercorp.lucy.security.xss.servletfilter.defender.XssPreventerDefender</class>
       </defender>
   </defenders>
 
    <!-- default defender 선언, 별다른 defender 선언이 없으면 default defender를 사용해 필터링 한다. -->
    <default>
        <defender>xssPreventerDefender</defender>
    </default>
 
    <!-- url 별 필터링 룰 선언 -->
    <url-rule-set>
       <!-- url disable이 true이면 지정한 url 내의 모든 파라메터는 필터링 되지 않는다. -->
       <url-rule>
           <url disable="true">/</url>
       </url-rule>
       <!-- <url-rule>
           <url disable="true">/app</url>
       </url-rule> -->
       <!-- 설정된 param은 필터링에서 제외된다. -->
       <url-rule>
           <url>/app/admin</url>
           <params>
                <param name="title" useDefender="false" />
            </params>
       </url-rule>
    </url-rule-set>
</config>

 

3) 그 다음, 어플리케이션에, 필터를 등록해준다, (Lucy filter가 작동하도록)

Web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- xss servlet filter -->
    <filter>
        <filter-name>xssEscapeServletFilter</filter-name>
        <filter-class>com.navercorp.lucy.security.xss.servletfilter.XssEscapeServletFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>xssEscapeServletFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

 

4) 인자, 스프링에서 동작할 필터를 만들어준다,

WebConfig.java

@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {

    private final ObjectMapper objectMapper;

    //Lucy Xss filter 적용
    @Bean
    public FilterRegistrationBean xssFilterBean(){
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(new XssEscapeServletFilter());
        registrationBean.setOrder(Ordered.LOWEST_PRECEDENCE);
        registrationBean.addUrlPatterns("*.do", "*.jsp");
        return registrationBean;
    }
}

 

이렇게 하면 루시 필터 적용이 끝난다!

 

하지만,,

 

lucy filter 적용 안될경우, Json 형식으로 받는건 아닌지 의심해 보아야한다. 

lucy xss servlet filter는 @requestBody를 필터링 하지 못한다.

 

나 같은 경우, 컨트롤러에서 RequsetBody로 DTO를 받아오기때문에, Json 형식이라 루시필터가 적용이 되지않았다.

아래의 블로그글을 참고하여, json 형식의 Xss 공격을 해결해보았다.

 

https://jojoldu.tistory.com/470

 

Spring Boot에서 JSON API에 XSS Filter 적용하기

일반적인 웹 애플리케이션에서 기본적으로 해야할 보안으로 XSS 방지가 있습니다. 기존에 많이들 알고 계시는 lucy filter의 단점은 이미 오명운 님께서 잘 정리해주셨기 때문에 한번쯤 읽어 보셔

jojoldu.tistory.com

 

 

5) XSS Json 공격 막기 (Requestbody)

WebConfig.java

@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {

    private final ObjectMapper objectMapper;

    //Lucy Xss filter 적용
    @Bean
    public FilterRegistrationBean xssFilterBean(){
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(new XssEscapeServletFilter());
        registrationBean.setOrder(Ordered.LOWEST_PRECEDENCE);
        registrationBean.addUrlPatterns("*.do", "*.jsp");
        return registrationBean;
    }

    //requestBody xss 필터 적용(json/api)
    @Bean
    public MappingJackson2HttpMessageConverter jsonEscapeConverter() {
        ObjectMapper copy = objectMapper.copy();
        copy.getFactory().setCharacterEscapes(new HtmlCharacterEscapes());
        return new MappingJackson2HttpMessageConverter(copy);
    }

}

HtmlCharacterEscapes.java

public class HtmlCharacterEscapes extends CharacterEscapes {
    private final int[] asciiEscapes;

    public HtmlCharacterEscapes() {
        // 1. XSS 방지 처리할 특수 문자 지정
        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;
    }

    @Override
    public int[] getEscapeCodesForAscii() {
        return asciiEscapes;
    }

    @Override
    public SerializableString getEscapeSequence(int ch) {
        return new SerializedString(StringEscapeUtils.escapeHtml4(Character.toString((char) ch)));
    }
}

 

위처럼하면, JSON 문자열 입력값을 Response를 클라이언트로 내보내는 단계에서 escape 할수 있다.

하지만, LocalDate 데이터타입같은 경우는, 거르지 못하기 때문에 추가 설정이 필요하다

 

AppConfig.java

@Configuration
public class AppConfig {

    @Bean
    @Primary
    public ObjectMapper objectMapper() {
        return new Jackson2ObjectMapperBuilder()
                .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                .modules(new JavaTimeModule())
                .timeZone("Asia/Seoul")
                .build();
    }
}

 

 


Spring boot XSS JSON 해결!

 

나이쓰!

 

 

네이버 깃헙: https://github.com/naver/lucy-xss-servlet-filter

lucy 사용법 : https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=jomer&logNo=221475344347

반응형