본문 바로가기

Springboot

Springboot lucy-xss-filter 적용

블로그를 제작하면서 CKEditor의 소스창 그대로 전송 또는 다른 input창에서 xss공격을 할 수 있기때문에,

lucy-xss-filter를 적용했습니다.

 

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

 

naver/lucy-xss-servlet-filter

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

github.com

1. pom.xml

<dependency>
	<groupId>com.navercorp.lucy</groupId>
	<artifactId>lucy-xss-servlet</artifactId>
	<version>2.0.0</version>
</dependency>

 

2. 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>
</config>

lucy-xss-servlet-filter-rule.xml파일을 resource폴더 아래에 만들어주세요

 

lucy-xss-servlet-filter는 크게 xssPreventerDefender 와 xssSaxFilterDefender, xssFilterDefender로 구분할수 있습니다.

xssPreventerDefender <>안에 있는 태그 전체를 필터링해주는 기능이며,

xssFilterDefender, xssSaxFilterDefender는 white 리스트를 통해 예외처리가 가능한 필터링 기능입니다.
현재는 필터링에 예외가 필요하지 않으니 xssPreventerDefender만 추가했습니다.

 

추후 CKEditor에서 예외처리할 태그 또는 파일업로드 등을 구현하게 될때 xssSaxFilterDefender, xssFilterDefender를 써볼 예정입니다.

 

※ 참고 (https://github.com/naver/lucy-xss-filter/blob/master/src/main/java/com/nhncorp/lucy/security/xss/XssPreventer.java)

이 코드는 추가하지 않아도 필터가 정상작동합니다. 참고만해주세요

package com.nhncorp.lucy.security.xss;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


/**
 * 이 클래스는 {@code Cross Site Scripting} 코드가 삽입된 {@code String} 데이터를
 * Apache Common Lang3을 사용해 신뢰할 수 있는 코드로 변환 시키는 기능을 제공한다. <br/><br/>
 * 기존의 XssFilter, XssSaxFilter와 주요 차이점은 모든 태그를 무력화시키는 점이다.
 * 이 클래스를 사용하는 방법은 다음과 같다.
 *
 * <pre>
 * ...
 *
 * String clean = XssPreventer.htmlEscaper(dirty);
 * String dirty = XssPreventer.htmlUnEscaper(clean);
 *
 * ...
 * </pre>
 *
 * @author Naver Labs
 *
 */
public class XssPreventer {

	private static final Log LOG = LogFactory.getLog(XssFilter.class);
	private static Pattern escapePattern = Pattern.compile("'");
	private static Pattern unescapePttern = Pattern.compile("&#39;");

	/**
	 * 이 메소드는 XSS({@code Cross Site Scripting})가 포함된 위험한 코드에 대하여
	 * 신뢰할 수 있는 코드로 변환하는 기능을 제공한다.
	 *  기존의 XssFilter, XssSaxFilter와 주요 차이점은 모든 태그를 무력화시키는 점이다.
	 *
	 * @param dirty
	 *            XSS({@code Cross Site Scripting})이 포함된 위험한 코드.
	 * @return 신뢰할 수 있는 코드.
	 */
	public static String escape(String dirty) {

		String clean = StringEscapeUtils.escapeHtml4(dirty);

		if (clean == null) {
			return null;
		}

		Matcher matcher = escapePattern.matcher(clean);

		if (matcher.find()) {
			return matcher.replaceAll("&#39;");
		}

		return clean;
	}

	/**
	 * 이 메소드는 XssPreventer를 수행하기 전의 코드로 원복하는 기능을 제공한다. <br/>
	 *
	 * @param clean
	 *            XssPreventer를 수행 후 문자열.
	 * @return XssPreventer를 수행 전의 문자열.
	 */
	public static String unescape(String clean) {

		String str = StringEscapeUtils.unescapeHtml4(clean);

		if (str == null) {
			return null;
		}

		Matcher matcher = unescapePttern.matcher(str);

		if (matcher.find()) {
			return matcher.replaceAll("'");
		}

		return str;
	}
}

내부적으로 Preventer를 간단히 보면 StringEscapeUtils.escapeHtml4(dirty)로 html4의 이스케이프 문자를 처리하여 

필터링 시점에 태그를 변경해서 저장하고, 본래로 돌아올때는 다시 원복하는 형식으로 진행됩니다.

 

 

3. application

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;

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

@SpringBootApplication
public class SecurityBlogApplication {
	public static void main(String[] args) {
		SpringApplication.run(SecurityBlogApplication.class, args);
	}
	
    @Bean
    public FilterRegistrationBean<XssEscapeServletFilter> getFilterRegistrationBean(){
        FilterRegistrationBean<XssEscapeServletFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new XssEscapeServletFilter());
        registrationBean.setOrder(1);
        registrationBean.addUrlPatterns("/write", "/restwrite");
        return registrationBean;
    }
}

 

application에서 filter를 등록시켜줍니다.

저는 경로로 /write, /restwrite를 추가하여 동기식글쓰기, 비동기식글쓰기를 테스트해보겠습니다.

동기 글쓰기
비동기 글쓰기
결과

결과는 다음과 같이 동기, 비동기 모두 적용된것을 볼 수 있습니다.