Spring Boot 에서 JSON 타입 XSS Prevention
Tags: Json, Web Development, Spring Boot, Xss Attack, Spring

다양한 방식이 존재하겠지만 HomoEfficio님의 Spring에서 JSON에 XSS 방지 처리 하기를 참조하여 MessageConverter와 Object Mapper를 활용하여 Escaping을 하고자 하였다.

차이점이 존재한다면, 이전의 Apache Commons Lang3 대신에 Apache Commons Text API를 이용하였다. 또한, WebMvcConfigurerAdapter 클래스의 메서드를 Overriding 하는 대신에 WebMvcConfigurer 인터페이스를 구현하여 Message Converter를 등록하여 사용하였다.

저의 경우 HomoEfficio님의 방식이 잘 작동하지 않았지만, 위와 같이 사용하였더니 잘 되었습니다.

HTMLCharacterEscapes 작성

package me.sml.xssdemo;



import com.fasterxml.jackson.core.SerializableString;

import com.fasterxml.jackson.core.io.CharacterEscapes;

import com.fasterxml.jackson.core.io.SerializedString;

import org.apache.commons.text.translate.AggregateTranslator;

import org.apache.commons.text.translate.CharSequenceTranslator;

import org.apache.commons.text.translate.EntityArrays;

import org.apache.commons.text.translate.LookupTranslator;



import java.util.Collections;

import java.util.HashMap;

import java.util.Map;





public class HTMLCharacterEscapes extends CharacterEscapes {



private final int[] asciiEscapes;



private final CharSequenceTranslator translator;



public HTMLCharacterEscapes() {



/**

* 여기에서 커스터마이징 가능

* 이전의 Apache Commons Lang3는 LookupTranslator의 파라미터로

* String[][]을 전달하였으나, 새로운 Apache Commons Text는 파라미터로

* Map<CharSequence, CharSequence>를 전달해야 한다.

* */

Map<CharSequence, CharSequence> customMap = new HashMap<>();

customMap.put("(", "(");

Map<CharSequence, CharSequence> CUSTOM_ESCAPE = Collections.unmodifiableMap(customMap);



// XSS 방지 처리할 특수 문자 지정

asciiEscapes = CharacterEscapes.standardAsciiEscapesForJSON();

asciiEscapes['<'] = CharacterEscapes.ESCAPE_CUSTOM;

asciiEscapes['>'] = CharacterEscapes.ESCAPE_CUSTOM;

asciiEscapes['&'] = CharacterEscapes.ESCAPE_CUSTOM;

asciiEscapes['('] = CharacterEscapes.ESCAPE_CUSTOM;



// XSS 방지 처리 특수 문자 인코딩 값 지정

translator = new AggregateTranslator(

new LookupTranslator(EntityArrays.BASIC_ESCAPE), // <, >, &, " 는 여기에 포함됨

new LookupTranslator(EntityArrays.ISO8859_1_ESCAPE),

new LookupTranslator(EntityArrays.HTML40_EXTENDED_ESCAPE),

new LookupTranslator(CUSTOM_ESCAPE)

);



}



@Override

public int[] getEscapeCodesForAscii() {

return asciiEscapes;

}



@Override

public SerializableString getEscapeSequence(int ch) {

return new SerializedString(translator.translate(Character.toString((char) ch)));

// 참고 - 커스터마이징이 필요없다면 아래와 같이 Apache Commons Text에서 제공하는 메서드를 써도 된다.

// return new SerializedString(StringEscapeUtils.escapeHtml4(Character.toString((char) ch)));

}

}

WebMvcConfigurer에 MessageConverter 등록

package me.sml.xssdemo;





import com.fasterxml.jackson.databind.ObjectMapper;

import com.fasterxml.jackson.databind.SerializationFeature;

import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

import com.navercorp.lucy.security.xss.servletfilter.XssEscapeServletFilter;

import org.springframework.boot.web.servlet.FilterRegistrationBean;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.http.converter.HttpMessageConverter;

import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;

import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;



import java.util.List;



@Configuration

public class WebConfig 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());



MappingJackson2HttpMessageConverter escapingConverter =

new MappingJackson2HttpMessageConverter();

escapingConverter.setObjectMapper(objectMapper);



return escapingConverter;

}



}

WebMvcConfigurer 인터페이스의 구현을 통해 Message Converter를 등록하도록 한다.

결과 확인

글 작성

JSON Response 확인

커스터마이징한 “(“가 “(”으로 바뀐 것을 확인 할 수 있다.

참고

Github 예제

https://github.com/Spring-Boot-Course/Spring-Boot-XSS-Protection