본문 바로가기
Backend/Spring

Spring boot에 lucy-xss-servlet-filter 적용하기

by 나는 유찌 2021. 7. 11.

 

XSS란 ?


Cross Site Scripting (XSS)은 악의적인 스크립트가 웹 사이트에 주입되는 공격이다. 

공격자가 웹 응용 프로그램을 사용하여 일반적으로 브라우저 측 스크립트 형태의 악성 코드를 다른 최종 사용자에게 보낼 때 발생한다.

XSS 공격은 웹 애플리케이션이 유효성을 검사하거나 인코딩하지 않고 생성하는 출력 내에서 사용자의 입력을 사용할 때 발생한다.

이 악성 스크립트는 해당 사이트에서 사용되는 쿠키, 세션 토큰, 다른 민감한 정보에 접근 할 수 있기 때문에 예방해야 한다.

출처 : https://owasp.org/www-community/attacks/xss/

 

 

 

예방 방법


NAVER에서 개발한 ‘lucy-xss-servlet-filter’라는 서블릿 필터 기반의 라이브러리가 존재한다.

개발자가 직접 경우의 수를 고려하여 들어오는 데이터를 인코딩 되도록 코드를 만들 수 있지만 html 태그는 무수하게 많으므로 놓치는 부분들이 존재 할 수 있다.

때문에 라이브러리 사용을 권장한다.

깃허브 주소 : https://github.com/naver/lucy-xss-servlet-filter

 

 

 

공격 형태


어떤식으로 XSS 공격이 이루어지는지 이론말고 실제로 알아보자.

 

1. DB에서 테이블을 하나 만들어주고 title, content 라는 컬럼을 생성해준다.

2. title이라는 컬럼에 ‘<img src="test" onmouseover="alert('??’)”/>’ 라는 데이터를 넣어주고 리스트로 불러오면 어떤 형식으로 나오는지 확인을 해본다.

 

TITLE이라는 컬럼에만 '<img src="test" onmouseover="alert('??')"/>' 데이터를 입력해준다.

lucy-xss-servlet-filter 라이브러리를 적용하기 전에는 DB에 해당 스크립트가 그대로 들어가는 걸 확인 할 수 있다.

lucy-xss-servlet-filter 적용 전 DB 조회

그럼 이 데이터를 화면에다가 출력을 해보자.

데이터 화면 출력

뭔일 !

위의 스크립트가 그대로 출력이 되면서 img 태그로 인식을 하고 스크립트대로 마우스 커서를 올리면 alert이 뜨게 되었다.

 

 

 

적용


NAVER 깃허브에 들어가보면 xml 파일로 설정하는 Spring 프레임워크로 코드가 짜여있다.

이것을 Java 파일로 설정하는 Spring boot 프레임워크로 적용하는 방법을 알아보자.

 

* 본인은 Maven으로 Spring boot 프로젝트를 생성하였다.

 

 

1. 우선 Maven에 'lucy-xss-servlet-filter' Dependency를 추가한 후 'maven update project'를 실행한다.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.5.2</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.does.xss</groupId>
	<artifactId>XSS</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>XSS</name>
	<description>XSS</description>
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
    	<-- 생략 -->
		<dependency>
			<groupId>com.navercorp.lucy</groupId>
			<artifactId>lucy-xss-servlet</artifactId>
			<version>2.0.0</version>
		</dependency>
	</dependencies>
    <!-- 생략 -->
</project>

 

2. 다음으로 Spring boot 프로젝트 안의 'resources' 폴더 아래에 다음과 같은 이름의 xml 파일 두 개를 생성한다.

(이름이 틀리면 인식을 하지 못하므로 이름을 반드시 다음과 같이 한다.)

 

2-1. lucy-xss-superset-sax.xml

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

<config xmlns="http://www.nhncorp.com/lucy-xss"
	extends="lucy-xss-default-sax.xml">

	<elementRule>
		<element name="body" disable="true" /> <!-- <BODY ONLOAD=alert("XSS")>, <BODY BACKGROUND="javascript:alert('XSS')"> -->
		<element name="embed" disable="true" />
		<element name="iframe" disable="true" /> <!-- <IFRAME SRC=”http://hacker-site.com/xss.html”> -->
		<element name="meta" disable="true" />
		<element name="object" disable="true" />
		<element name="script" disable="true" /> <!-- <SCRIPT> alert(“XSS”); </SCRIPT> -->
		<element name="style" disable="true" />
		<element name="link" disable="true" />
		<element name="base" disable="true" />
	</elementRule>
	
	<attributeRule>
		<attribute name="data" base64Decoding="true">
			<notAllowedPattern><![CDATA[(?i:s\\*c\\*r\\*i\\*p\\*t\\*:)]]></notAllowedPattern>
			<notAllowedPattern><![CDATA[(?i:d\\*a\\*t\\*a\\*:)]]></notAllowedPattern>
			<notAllowedPattern><![CDATA[&[#\\%x]+[\da-fA-F][\da-fA-F]+]]></notAllowedPattern>
		</attribute>
		<attribute name="src" base64Decoding="true">
			<notAllowedPattern><![CDATA[(?i:s\\*c\\*r\\*i\\*p\\*t\\*:)]]></notAllowedPattern>
			<notAllowedPattern><![CDATA[(?i:d\\*a\\*t\\*a\\*:)]]></notAllowedPattern>
			<notAllowedPattern><![CDATA[&[#\\%x]+[\da-fA-F][\da-fA-F]+]]></notAllowedPattern>
		</attribute>
		<attribute name="style">
			<notAllowedPattern><![CDATA[(?i:j\\*a\\*v\\*a\\*s\\*c\\*r\\*i\\*p\\*t\\*:)]]></notAllowedPattern>
			<notAllowedPattern><![CDATA[(?i:e\\*x\\*p\\*r\\*e\\*s\\*s\\*i\\*o\\*n)]]></notAllowedPattern>
			<notAllowedPattern><![CDATA[&[#\\%x]+[\da-fA-F][\da-fA-F]+]]></notAllowedPattern>
		</attribute>
		<attribute name="href">
  		 <notAllowedPattern><![CDATA[(?i:j\\*a\\*v\\*a\\*s\\*c\\*r\\*i\\*p\\*t\\*:)]]></notAllowedPattern>
 		</attribute>
	</attributeRule>

</config>

 

2-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>

       <!-- 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>

    <!-- global 필터링 룰 선언 -->
    <global>
        <!-- 모든 url에서 들어오는 globalParameter 파라메터는 필터링 되지 않으며 
                또한 globalPrefixParameter로 시작하는 파라메터도 필터링 되지 않는다. -->
        <params>
            <param name="globalParameter" useDefender="false" />
            <param name="globalPrefixParameter" usePrefix="true" useDefender="false" />
        </params>
    </global>

    <!-- url 별 필터링 룰 선언 -->
    <url-rule-set>
       
       <!-- url disable이 true이면 지정한 url 내의 모든 파라메터는 필터링 되지 않는다. -->
       <url-rule>
           <url disable="true">/disableUrl1.do</url>
       </url-rule>
       
        <!-- url1 내의 url1Parameter는 필터링 되지 않으며 또한 url1PrefixParameter로 시작하는 파라메터도 필터링 되지 않는다. -->
        <url-rule>
            <url>/url1.do</url>
            <params>
                <param name="url1Parameter" useDefender="false" />
                <param name="url1PrefixParameter" usePrefix="true" useDefender="false" />
            </params>
        </url-rule>
        
        <!-- url2 내의 url2Parameter1만 필터링 되지 않으며 url2Parameter2는 xssSaxFilterDefender를 사용해 필터링 한다.  -->
        <url-rule>
            <url>/url2.do</url>
            <params>
                <param name="url2Parameter1" useDefender="false" />
                <param name="url2Parameter2">
                    <defender>xssSaxFilterDefender</defender>
                </param>
            </params>
        </url-rule>
    </url-rule-set>
</config>

NAVER 깃허브에서 그대로 가져왔다. 주석이 잘 되어있어서 만족스럽다. 푸하하!

 

 

 

3. 다음으로 설정 Class 파일을 생성해준다. 본인은 'XssConfig'라는 이름으로 생성했다.

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.navercorp.lucy.security.xss.servletfilter.XssEscapeServletFilter;

@Configuration
public class XssConfig implements WebMvcConfigurer {
	@Bean
	public FilterRegistrationBean<XssEscapeServletFilter> filterRegistrationBean() {
		FilterRegistrationBean<XssEscapeServletFilter> filterRegistration = new FilterRegistrationBean<>();
		filterRegistration.setFilter(new XssEscapeServletFilter());
		filterRegistration.setOrder(1);
		filterRegistration.addUrlPatterns("/*");
		return filterRegistration;
	}
}

파일 첨부 기능이 있다면 Xss 필터가 적용이 안된다고 한다. 그럴 경우 여기서 Filter를 순서에 맞게 추가를 해줘야 된다고 하는데 중요한건 테스트로 만든 프로젝트에서 파일 첨부 기능을 추가해도 Xss 필터가 작동이 된다.. 일단 기본만 올려두고 나중에 해당 내용은 더 추가하도록 하겠다.

 

 

 

4. 테스트

동일하게 TITLE이라는 컬럼에만 '<img src="test" onmouseover="alert('??')"/>'이라는 데이터를 넣어준다.

DB를 조회해보자.

lucy-xss-servlet-filter 적용 후 DB 조회

1번은 적용 전의 데이터고 2번이 적용 후의 데이터이다.

'<', ' " ' 등 이런 문자들이 전부 필터를 거쳐 변경되어 DB에 저장 된 것을 확인 할 수 있다.

 

그럼 이 데이터를 화면에 리스트로 뽑아보자.

데이터 화면 출력

* 본인은 화면을 타임리프를 사용했는데 참고로 하나 말하자면 타임리프로 사용하는 경우 'th:text'로 출력하는게 아니라 'th:utext'로 출력하면 된다.

아까와 같이 이미지 태그를 인식하지 않고 글자로 그대로 출력해준다!