스프링

blog post

 

 xss 보안 설정에 네이버에서 제공하는 lucy 를 사용하고 자 한다.

 

lucy 는 두가 지가 있는데 ,  전체를 적용하는   lucy-xss-servlet-filter 와

일일 개별적으로 적용하는 lucy-xss-servlet-filter 가 있다.

 

네이버에서 설명하는 내용이다.

신규로 개발하는 서비스에는 lucy-xss-servlet-filter를 사용하는 것을 추천하지만, 기존 잘 운영되는 시스템에 lucy-xss-servlet-filter를 사용하는 것은 추천하지 않습니다. 입력 파라메터가 전부 필터링 되기 때문에 서비스가 잘 동작하지 않는 의도치 않은 결과가 발생할 수 있기 때문입니다.

기존 시스템에 적용하시려면 lucy-xss-filter를 사용해 개발자가 일일히 필터링 하는 방식을 추천드립니다. 하지만 이 방법은 개발자의 집중력를 믿어야 하기 때문에 언젠가는 뚫릴 수 밖에 없는 방식입니다. 그렇기 때문에 시간을 충분히 확보하실 수 있으시거나 기존에 서블릿 필터 기반으로 XSS 공격을 방어하셨다면 이번 기회에 lucy-xss-servlet-filter를 적용해 XSS 공격에서 해방되시는 걸 추천드립니다.

 

 

1. lucy-xss-filter

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

 

2. lucy-xss-servlet-filter

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

 

문제는 에디터에서 적용할 경우 예외를 해 줘야 한다.

 

 

 

Lucy-XSS Filter, Lucy-XSS Preventer 선택 기준 

https://github.com/naver/lucy-xss-filter/blob/master/docs/manual/kr/01.%20summary/1.3%20selection%20criterion.md

 

XSS Filter는 보안에 중점을 두면서도, HTML 태그 또한 정상 동작하도록 하는 White List 방식의 XSS 공격 방어 라이브러리이다.

XSS Preventer는 파라미터로 받은 문자열을 Escape(<→< >→> "→" '→') 하는 XSS공격 방어 라이브러리이다. 즉 HTML이 아닌 단순 텍스트 파라미터에 대해서는 XSS Preventer를 사용해 전체를 Escaping 하는 것이 올바른 대응 방법이고

게시판, 메일, 방명록 등 HTML 태그 기능이 필요한 서비스는 XSS Filter를 사용해 필터링 하는 것이 효과적인 방법이므로 개발자는 두 가지 상황을 고려해 방어 라이브러리를 사용해야 한다.

마지막으로 사용자 입력데이터가 뷰에 다시 노출시킬 목적이 아닌 Business Logic에만 쓰이는 데이터일 경우에는 둘 다 사용하지 말아야 한다. 불필요한 eacape/unescape이 발생해 원본데이터가 훼손될 수 있다.

 

 

빠른 사용법 가이드

링크  => Quick Start Guide

 

 

자세한 내용은  위 링크를 보면 될 것이다.

 

방법 

내가 사용한 방법은   두가지 다 사용하는 것이다.

특히,  editor 를 사용시에는  필터가 적용되면 안 된다. 왜냐하면은 에디터내에서 필터기능을 하기 때문에

xss 필터를 적용시 

<pre>

&lt;script&gt;alert(&quot;테스트&quot;);&lt;/script&gt; 

</pre>

이와 같이 출력되어 나타나서 보기가 안 좋다.

따라서,   루시  서블릿 필터로 전체 적용을 하고 editor 영역만  컨트롤에서 다음과 같이

unescape () 처리 하는 코드만 작성해 주자.   그러면  깨끗하게 처리되어 진다.

XssPreventer.unescape("");

ex) vo.setMessage(XssPreventer.unescape(vo.getMessage()));

 

1. 라이브러리를 의존성 주입을 하자.

	<!-- lucy xss 설치 -->
		<dependency>
			<groupId>com.navercorp.lucy</groupId>
			<artifactId>lucy-xss</artifactId>
			<version>1.6.3</version>
		</dependency>
		
		<!-- lucy-xss-servlet -->
		<dependency>
			<groupId>com.navercorp.lucy</groupId>
			<artifactId>lucy-xss-servlet</artifactId>
			<version>2.0.0</version>
		</dependency>

 

 

 

2. web.xml 에 다음과 같이 설정하자.

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

 

   <!-- 한글 처리를 위한 인코딩 필터 -->
	<!-- xssEscapeServletFilter는 CharacterEncodingFilter 뒤에 위치해야 한다. -->
	<filter>
		<filter-name>encodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>encodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

	<filter>
		<filter-name>xssEscapeServletFilter</filter-name>
		<filter-class>com.navercorp.lucy.security.xss.servletfilter.XssEscapeServletFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>xssEscapeServletFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

 

 

 

3. resources /  lucy-xss-servlet-filter-rule.xml  카피해서 복사

 

아래 내용을 카피해서 복사해서 넣자.

  servelt-filter 사용시 여기 내용을 변경 하면 되는데, 

나는 이것보다   editor 사용시

XssPreventer.unescape(""); 적용이 간편한것 같다.

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

 

 

설정은 다 되었다.  다음 내용은 테스트 이다.  

중요한것은  글을 작성시 editor  사용하는 경우이다.   mybatis 로  sql  insert  되기전에 

XssPreventer.unescape() 처리를 해주자.   나는  컨트롤 영역에서 해주었다.

 

 

 

4. 테스트 1 

lucy-xss-filter 설정 되었는지 테스트 영역에서

다음과 같은 코드로 테스트를 실행 해 보자.  아래 내용을 복사 하자.

TestXss

package net.macaronics.web.xss;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.nhncorp.lucy.security.xss.XssFilter;
import com.nhncorp.lucy.security.xss.XssPreventer;
import com.nhncorp.lucy.security.xss.XssSaxFilter;

import junit.framework.Assert;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"file:src/main/webapp/WEB-INF/spring/**/*.xml"})
public class TestXss {

	@SuppressWarnings("deprecation")
	@Test
	public void testXssPreventer() {
		String dirty = "\"><script>alert('xss');</script>";
		String clean = XssPreventer.escape(dirty);
			
		Assert.assertEquals(clean, "&quot;&gt;&lt;script&gt;alert(&#39xss&#39);&lt;/script&gt;");
		Assert.assertEquals(dirty, XssPreventer.unescape(clean));
	}
	
	@Test
	public void pairQuoteCheckOtherCase() {
		XssFilter filter = XssFilter.getInstance("lucy-xss-superset.xml");
		String dirty = "<img src=\"<img src=1\\ onerror=alert(1234)>\" onerror=\"alert('XSS')\">";
		String expected = "<img src=\"\"><!-- Not Allowed Attribute Filtered ( onerror=alert(1234)) --><img src=1\\>\" onerror=\"alert('XSS')\"&gt;";
		String clean = filter.doFilter(dirty);
		Assert.assertEquals(expected, clean);
			
		dirty = "<img src='<img src=1\\ onerror=alert(1234)>\" onerror=\"alert('XSS')\">";
		expected = "<img src=''><!-- Not Allowed Attribute Filtered ( onerror=alert(1234)) --><img src=1\\>\" onerror=\"alert('XSS')\"&gt;";
		clean = filter.doFilter(dirty);
		Assert.assertEquals(expected, clean);
	}
	
	
	@Test
	public void testSuperSetFix() {
		XssSaxFilter filter = XssSaxFilter.getInstance("lucy-xss-superset-sax.xml");
		String clean = "<TABLE class=\"NHN_Layout_Main\" style=\"TABLE-LAYOUT: fixed\" cellSpacing=\"0\" cellPadding=\"0\" width=\"743\">" + "</TABLE>" + "<SPAN style=\"COLOR: #66cc99\"></SPAN>";
		String filtered = filter.doFilter(clean);
		Assert.assertEquals(clean, filtered);
	}
	
	
	
}




 

 

실제로 데이터가 DB 에 어떻게 적용되는지 그리고 어떻게 출력되는 지 테스트를 해보자.

자세한 내용은

home pc 에서 노트북으로 변경 으로 

나의 깃허브의 test02 프로젝트를  복사해서 test 프로젝트에서 개발 하였다.

https://github.com/braverokmc79/spring_simple_blog

 

SQL

create table tbl_message(
	
	mid int not null auto_increment,
	targetid varchar(50) not null,
	sender varchar(50) not null,
	message text not null,
	opendate timestamp,
	senddate timestamp not null default now(),
	primary key(mid)

);

 

messageMapper.xml 

	 <insert id="create">
	 	insert into tbl_message (targetid, sender, message)
	 	 values (#{targetid} , #{sender}, #{message})
	 </insert>

 

XssTestController

package test.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.nhncorp.lucy.security.xss.XssPreventer;

import test.domain.MessageVO;
import test.service.MessageService;

@Controller
public class XssTestController {

	@Autowired
	private MessageService service;
	
	
	@RequestMapping(value="xssform",method=RequestMethod.GET)
	public String xssTest() throws Exception{
		return "test/xss";
	}
	
	@RequestMapping(value="xssform", method=RequestMethod.POST)
	public String xssTes2(Model model, MessageVO vo) throws Exception{
		
		vo.setMessage(XssPreventer.unescape(vo.getMessage()));
		service.create(vo);
		model.addAttribute("message", service.listMessage());
		
		return "test/xssresult";
	}
		
	
	
	
	
	
}

글 작성시 에  POST

vo.setMessage(XssPreventer.unescape(vo.getMessage()));

코드를 볼 수 있다.   editor 를 적용한 message  에 만  예외처리를 한것이다.

 

 

xss.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Macaronics.net</title>
<!-- 합쳐지고 최소화된 최신 CSS -->
<link rel="stylesheet"
	href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">

<!-- 부가적인 테마 -->
<link rel="stylesheet"
	href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap-theme.min.css">

<!-- 합쳐지고 최소화된 최신 자바스크립트 -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
<script src="//cdn.ckeditor.com/4.7.3/standard/ckeditor.js"></script>
</head>
<body>

	<h1 class="text-center">XSS Test</h1>
	<div class="row">
		<div class="col-xl-2 col-sm-2"></div>
		<div class="col-xl-8 col-sm-8">
			<form action="xssform" method="post">
				<table class="table">
					<tr>
						<td>targetid</td>
						<td><input type="text" class="form-control" value="" name="targetid"></td>
					</tr>


					<tr>
						<td>내용</td>
						<td class="span12"><textarea rows="5" class="form-control"
								name="message"></textarea> <!-- textarea 를 ckeditor 로 변경 시킴 -->
					</tr>
					<tr>
						<td>sender</td>
						<td><input type="text" class="form-control" value="admin" name="sender"></td>
					</tr>

					<tr>
						<td colspan="2"><input type="submit" value="전송"></td>
					</tr>
				</table>
			</form>
		</div>
	</div>


<script>
	CKEDITOR.replace("message", {

		filebrowserUploadUrl : "/ckeditorupload3"
	// filebrowserImageUploadUrl: 'MacaronicsServlet?command=ckeditor_upload'	
	});
</script>

</body>
</html>

 

xssresult.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>	
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Macaronics.net</title>
<!-- 합쳐지고 최소화된 최신 CSS -->
<link rel="stylesheet"
	href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">

<!-- 부가적인 테마 -->
<link rel="stylesheet"
	href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap-theme.min.css">

<!-- 합쳐지고 최소화된 최신 자바스크립트 -->
<script
	src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>

</head>
<body>

	<h1 class="text-center">XSS Test</h1>
	<div class="row">
		<div class="col-xl-2 col-sm-2"></div>
		<div class="col-xl-8 col-sm-8">
			<c:forEach items="${message }" var="ms">
				<table class="table">
				    <tr>
				    	<td>아이디</td>
				    	<td>${ms.mid }</td>
				    </tr>
				    
					<tr>
						<td>targetid</td>
						<td>${ms.targetid}</td>
					</tr>
					<tr>
						<td>내용</td>
						<td class="span12">${ms.message}</td>
					</tr>
					<tr>
						<td>sender</td>
						<td>${ms.sender}</td>
					</tr>

					<tr>
						<td colspan="2"><a href="xssform" >다시 테스트</a></td>
					</tr>
			</table>
			</c:forEach>
		</div>
	</div>




</body>
</html>

 

 

테스트한 결과 이미지 이다.

 

 

 

 

 

 

 

 

제작 : macaronics.net - Developer  Jun Ho Choi

소스 :  소스가 필요한 분은 비밀 댓글로 작성해 주세요.

 

 

 

 

about author

PHRASE

Level 60  라이트

유익하지 못한 천 마디 말보다 마음의 평안을 얻는 한 마디야말로 생명의 말이다. -법구경

댓글 ( 2)

댓글 남기기

작성