ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • XSS 공격에 대한 방어
    스터디 2018. 9. 4. 16:04

    프로젝트를 진행하며 XSS(Cross Site Scripting) 공격에 대한 방어를 하기 위해 네이버에서 개발한 Lucy-Xss-Servlet-Filter 라이브러리를 사용했습니다. 해당 라이브러리는 웹어플리케이션으로 들어오는 모든 요청 파라미터에 대해 기본적으로 XSS 방어 필터링을 수행합니다. 하지만 Lucy-Xss-Servlet-Filter 라이브러리는 form-data에 대해서만 적용되고 JSON에 대해서는 처리해주지 않는다는 단점이 있습니다. 이를 위해 별도로 Request Body로 넘어오는 JSON에 대한 필터를 등록해줬습니다. (마지막 부분에는 Lucy-Xss-Servlet-Filter를 사용하지않고 XSS 공격을 방어하는 법을 소개하겠습니다.)



    Lucy-Xss-Servlet-Filter 라이브러리를 사용한 경우


    우선, 방어를 위해 사용되어지는 XssEscapeServletFilter빈을 사용하기 위해서는 아래와 같이 의존성을 설정해 줍니다.

    // xss-filter
    compile('com.navercorp.lucy:lucy-xss-servlet:2.0.0')


    아래 코드는 XSS 필터를 설정하는 부분입니다.

    @Configuration
    public class WebMvcConfig implements WebMvcConfigurer {
    ...

    // Form data
    @Bean
    public FilterRegistrationBean getXssEscapeServletFilterRegistrationBean() {
    FilterRegistrationBean registrationBean = new FilterRegistrationBean();
    registrationBean.setFilter(new XssEscapeServletFilter());
    registrationBean.setOrder(1);
    registrationBean.addUrlPatterns("/api/reviews", "/chat"); //filter를 거칠 url patterns
    return registrationBean;
    }

    // Request Body JSON
    @Bean
    public FilterRegistrationBean getRequestBodyXSSFileterRegistrationBean() {
    FilterRegistrationBean registrationBean = new FilterRegistrationBean();
    registrationBean.setFilter(new RequestBodyXSSFileter());
    registrationBean.addUrlPatterns("/api/reviews/update", "/chat"); //filter를 거칠 url patterns
    return registrationBean;
    }



    /resource 폴더 밑에 lucy-xss-servlet-filter-rule.xml 파일을 생성하고, 자신의 프로젝트에 맞게 필터링 룰을 작성합니다.

    <?xml version="1.0" encoding="UTF-8"?>
    <config xmlns="http://www.navercorp.com/lucy-xss-servlet">
    <!-- XssPreventer 등록 -->
    <defenders>
    <!-- XssPreventer 등록 -->
    <defender>
    <name>xssPreventerDefender</name>
    <class>com.navercorp.lucy.security.xss.servletfilter.defender.XssPreventerDefender</class>
    </defender>

    <!-- XssSaxFilter 등록 -->
    <defender>
    <name>xssSaxFilterDefender</name>
    <class>com.navercorp.lucy.security.xss.servletfilter.defender.XssSaxFilterDefender</class>
    <init-param>
    <param-value>lucy-xss-sax.xml</param-value> <!-- lucy-xss-filter의 sax용 설정파일 -->
    <param-value>false</param-value> <!-- 필터링된 코멘트를 남길지 여부, 성능 효율상 false 추천 -->
    </init-param>
    </defender>

    <!-- XssFilter 등록 -->
    <defender>
    <name>xssFilterDefender</name>
    <class>com.navercorp.lucy.security.xss.servletfilter.defender.XssFilterDefender</class>
    <init-param>
    <param-value>lucy-xss.xml</param-value> <!-- lucy-xss-filter의 dom용 설정파일 -->
    <param-value>false</param-value> <!-- 필터링된 코멘트를 남길지 여부, 성능 효율상 false 추천 -->
    </init-param>
    </defender>
    </defenders>

    <!-- default defender 선언, 별다른 defender 선언이 없으면 default defender를 사용해 필터링 한다. -->
    <default>
    <defender>xssPreventerDefender</defender>
    </default>


    <!-- url 별 필터링 룰 선언 -->
    <url-rule-set>
    <url-rule>
    <url>/api/reviews</url>
    <params>
    <param name="contents" useDefender="true"/>
    <param name="orderFoodId" useDefender="false"/>
    <param name="starPoint" useDefender="false"/>
    <param name="page" useDefender="false"/>
    <param name="filterId" useDefender="false"/>
    </params>
    </url-rule>

    <url-rule>
    <url>/api/reviews/update</url>
    <params>
    <param name="contents" useDefender="true"/>
    <param name="orderFoodId" useDefender="false"/>
    <param name="starPoint" useDefender="false"/>
    </params>
    </url-rule>

    <url-rule>
    <url>/chat</url>
    <params>
    <param name="message" useDefender="true"/>
    <param name="roomId" useDefender="false"/>
    </params>
    </url-rule>

    </url-rule-set>
    </config>


    그런데 여기서 문제가 하나 발생했습니다. 


    진행하고있는 프로젝트에서 채팅을 위해 웹 소켓을 사용하고 있는데, 저는 당연히 Lucy-Xss-Servlet-Filter 라이브러리를 적용하면 채팅에서도 XSS 공격에 대한 방어가 될 것이라고 생각했습니다. 하지만 예상과 달리 웹소켓에서는 Lucy-Xss-Servlet-Filter 라이브러리가 적용되지 않았습니다. 


    따라서 채팅에서는 XSS 공격을 어떤식으로 방어 할지 고민한 끝에, Jackson Object매퍼가 JSON 문자열을 생성할 때 XSS 방지 처리를 해주면 될 것 같다는 생각이 들었습니다. 따라서, Jackson의 com.fasterxml.jackson.core.io.CharacterEscapes를 상속하는 HTMLCharacterEscapes 클래스를 직접 만들어서 처리해야 할 특수문자를 지정해줬습니다.

    public class HTMLCharacterEscapes extends CharacterEscapes {

    private final int[] asciiEscapes;

    public HTMLCharacterEscapes() {
    // XSS 방지 처리할 특수 문자 지정
    asciiEscapes = CharacterEscapes.standardAsciiEscapesForJSON();
    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)));
    }
    }


    그 후, ObjectMapper에 생성한 클래스를 설정하고 ObjectMapper를 MessageConverter에 등록해서 Response가 클라이언트에 나가기 전에 XSS 공격에 대한 방어 처리를 해줬습니다.


    @Slf4j
    @Configuration
    @EnableWebSocketMessageBroker
    public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    ...

    @Override
    public boolean configureMessageConverters(List<MessageConverter> messageConverters) {
    messageConverters.add(escapingConverter());
    return true;
    }

    private MessageConverter escapingConverter() {
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.getFactory().setCharacterEscapes(new HTMLCharacterEscapes());

    MappingJackson2MessageConverter escapingConverter =
    new MappingJackson2MessageConverter();
    escapingConverter.setObjectMapper(objectMapper);

    return escapingConverter;
    }


    Lucy-Xss-Servlet-Filter 라이브러리를 사용하지 않은 경우


    여기서 힌트를 얻어, 웹소켓 뿐 만 아니라 웹에서도 위와 같이 HttpMessageConverter를 이용하면 Lucy-Xss-Servlet-Filter 라이브러리를 걷어낼 수 있지 않을까? 라는 생각을 하게 됐습니다. 그리하여 아래와 같이 적용했더니 정상적으로 동작하는 것을 확인할 수 있었습니다.

    @Configuration
    public class WebMvcConfig implements WebMvcConfigurer {
    ...

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    converters.add(escapingConverter());

    }

    @Bean
    public HttpMessageConverter escapingConverter() {
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.getFactory().setCharacterEscapes(new HTMLCharacterEscapes());
    objectMapper.registerModule(new JavaTimeModule());
    objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

    MappingJackson2HttpMessageConverter escapingConverter =
    new MappingJackson2HttpMessageConverter();
    escapingConverter.setObjectMapper(objectMapper);

    return escapingConverter;
    }




    참고

    https://github.com/naver/lucy-xss-servlet-filter

    https://homoefficio.github.io/2016/11/21/Spring%EC%97%90%EC%84%9C-JSON%EC%97%90-XSS-%EB%B0%A9%EC%A7%80-%EC%B2%98%EB%A6%AC-%ED%95%98%EA%B8%B0/

    '스터디' 카테고리의 다른 글

    잘못된 URI 접근 시, 커스텀 에러 페이지로 이동  (1) 2018.09.04

    댓글

Designed by Tistory.