본문 바로가기
Spring

XSS Filter 적용하기 (lucy-xss-servlet-filter)

by 밝지 2023. 4. 25.
728x90
반응형

XSS ? 

Cross Site Scripting의 약자로, 웹 어플리케이션에 악의적으로 스크립트를 삽입하여 공격하는 기법이다. 

 

 

Lucy-xss-servlet-filter ?

Naver에서 제공하는 오픈소스. 웹 어플리케이션으로 들어오는 모든 요청 파라미터에 대해 기본적인 XSS 방어 필터링을 수행한다. 아래와 같은 필터링 제외 설정을 제공한다.

  • 설정한 url을 필터링에서 제외
  • 설정한 prefix로 시작하는 파라미터를 필터링에서 제외
  • 설정한 파라미터를 필터링에서 제외

이전에 lucy-xss-filter 라이브러리가 있었는데, 개발자가 수동으로 치환 로직을 일일히 추가해야 하는 방식으로 조치가 누락되는 경우가 많았다. 이에 대한 개선책으로 '자바 서블릿 필터 기반'의 라이브러리로 새로 나온 것이 'lucy-xss-servlet-filter'

[장점]

  • XML(또는 gradle & yml) 설정만으로 XSS 방어 가능
  • 비즈니스 레이어의 코드를 수정할 필요 없음
  • 개발자가 XSS 방어를 신경쓰지 않아도 됨
  • XSS 방어가 누락되지 않음
  • 불필요한 곳에 XSS 방어 코드 적용 X
  • 설정 파일만 보면 XSS 방어 절차 확인이 가능함

[단점]

  • 파라미터명 관리가 필요함 (url, 파라미터prefix 등으로 필터링 대상을 설정함)
  • 일괄 적용되기 때문에 필터링 룰 정의가 중요

(참고: https://github.com/naver/lucy-xss-servlet-filter)

 

 

Springboot에 적용하기

pom.xml, web.xml 추가 방식은 여기 참고: https://github.com/naver/lucy-xss-servlet-filter

 

GitHub - naver/lucy-xss-servlet-filter

Contribute to naver/lucy-xss-servlet-filter development by creating an account on GitHub.

github.com

 

 

1) build.gradle 에 dependency 추가하기

build.gradle 파일에 아래와 같이 dependency를 추가한다.

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

 

 

2) application.yml에 패키지명.web.xss.use-xss-filter 값을 true로 설정한다. (설정 값이 없으면 false 로 동작함)

-> true 로 설정하면 lucy-xss-sax.xml 파일에 설정한 정책을 기준으로 동작한다.

 

 

3) 적용할 HTML tag 설정하기

예외처리가 필요한 HTML tag를 lucy-xss-sax.xml 파일에 등록한다. Naver에서 제공하는 기본 설정이 있음. 적용 제외 하려면 disable="false"로 하거나 (disable="true" 여야 예외처리됨) 해당 행을 삭제하면 된다. 

 

 

4) XSS Filter를 servletFilter에 등록하기

참고: https://github.com/naver/lucy-xss-servlet-filter/blob/master/doc/manual.md

 

GitHub - naver/lucy-xss-servlet-filter

Contribute to naver/lucy-xss-servlet-filter development by creating an account on GitHub.

github.com

예)

<?xml version="1.0" encoding="UTF-8"?>

<config xmlns="http://www.navercorp.com/lucy-xss-servlet">
	<defenders>

		<!-- 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>true</param-value>        <!-- true: 주석없음, false: 주석있음. -->
			</init-param>
		</defender>

	</defenders>

	<!-- default defender 선언, 필터링 시 지정한 defender가 없으면 여기 정의된 default defender를 사용해 필터링 한다. -->
	<default>
		<defender>xssSaxFilterDefender</defender>
	</default>
</config>

 

 

5) XSS Filter를 Bean으로 등록하기

 파라미터에 설정 값이 적용될 수 있도록 Bean으로 등록한다.

예)

@Configuration
public class XssConfig implements WebMvcConfigurer {
	@Value("${패키지명.web.xss.use-xss-filter:false}")
    private boolean useXssFilter;
    
    public XssConfig() {
    }

    @Bean
    public FilterRegistrationBean getFilterRegistrationBean() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(new XssEscapeServletFilter());
        if (this.useXssFilter) {
            registrationBean.addUrlPatterns(new String[]{"/*"});
        } else {
            registrationBean.addUrlPatterns(new String[]{"/_all_not_url_match_"});
        }

        return registrationBean;
    }
}

 

 

6) XSS Filter를 RequestBody에 적용하기

RequestBodyAdvice를 상속받은 RequestBodyXssAdvice 파일을 만들어서 설정하고, @RestControllerAdvice 지정.

예) 

@RestControllerAdvice
public class RequestBodyXssAdvice implements RequestBodyAdvice {
	
    private final Environmet env;
    private static final XssSaxFilter xssSaxFilter = XssSaxFilter.getInstance("lucy-xss-sax.xml", true);
        
    public RequestBodyXssAdvice(Environment env) {
    	this.env = env;
    }
    
    public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
    	return true;
    }
    
   public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
        return httpInputMessage;
    }

    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        String useXssFilterStr = this.env.getProperty("패키지명.web.xss.use-xss-filter");
        boolean useXssFilter = "true".equalsIgnoreCase(useXssFilterStr);
        if (useXssFilter) {
            //String jsonStr = body to json String 작업
            log.debug(".afterBodyRead(...): jsonStr = {}", jsonStr);
            String xssEscapedJsonStr = xssSaxFilter.doFilter(jsonStr);
            log.debug(".afterBodyRead(...): xssEscapedJsonStr = {}", xssEscapedJsonStr);
            //return toObject(xssEscapedJsonStr, targetType); 작업
        } else {
            return body;
        }
    }

    public Object handleEmptyBody(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return body;
    }
    

}
728x90
반응형

'Spring' 카테고리의 다른 글

웹서버, 웹애플리케이션서버, CGI  (0) 2023.05.21
웹 서버 - 외장서버 vs 내장서버  (0) 2023.05.21
application.properties VS application.yml  (0) 2023.04.24
스프링 AOP  (0) 2023.03.05
스프링, 스프링 부트의 등장  (0) 2023.03.05