ckeditor로 글 등록하고 등록한 글을 볼 때 html 태그가 그대로 보이는 경우 참고 글

전자정부 프레임워크는 기관 웹페이지를 구성하는데 많이 사용하는 만큼 보안취약점과 웹취약점과 관련해서 여러가지 구성요소들이 있습니다. 그 중애 xss방지 처리 관련하여 글을 등록하고 화면단에서

<c:out value='${result.nttCn}' escapeXml='false' />

이런 jstl의 c:out 태그에 escapeXml을 false로 처리해줘서(Default는 true) 로 태그값을 원하는 대로 뿌려주는 방식을 계속해서 사용해 오고 있었는데 전자정부프레임워크의 버전을 올리면서(3.10으로) 이렇게 처리를 해줘도 HTMLTagFilter에 걸려서 ckeditor로 편집한 내용이 html tag들이 적용되지 않은 상태로 표출되는 상황에 직면하게 되었습니다.


위에 이미지처럼 내용에 이미지가 HTML tag가 적용되지 않는 모습입니다. 지금부터 xss란 무엇이고 이러한 xss를 방지하기 위해 전자정부 프레임워크에서 제안하는 처리과정들, 그리고 위에 이미지의 상황에 내가 원하는 화면을 보여주기 위해 처리할 수 있는 방법들을 정리해보고자 합니다.

XSS(Cross-Site Scripting)이란.

XSS(Cross-Site Scripting) 이란 웹 애플리케이션에서 일어나는 취약점으로 관리자가 아닌 권한이 없는 사용자가 웹 사이트에 스크립트를 삽입하는 공격 기법입니다. 대부분 사용자가 글을 쓰고 읽을 수 있는 게시판에 많이 발생하지만, 사용자의 입력 값을 웹 페이지에 보여주는 곳에서도 발생합니다. 악의적인 사용자가 C&C 서버로 리다이렉션 하기 위해 리다이렉션 스크립트를 주입하여 중간 경유지로 활용하기도 하고, 사용자의 쿠키를 탈취하여 세션 하이재킹(Session Hijacking) 공격을 수행하기도 합니다.

여기서 포인트는 script를 삽입하는 공격기법이란 것 입니다.
<script>, <a href="javascript: >, <img src="#"의 <onclick, onload, onerror> 처럼 악성 스크립트를 주입하기 위해 html태그를 사용하는 것을 보실 수 있습니다. 이러한 악의적인 공격을 막기위해 전자정부 프레임워크에서는 XSS방지로 html tag를 제한합니다. 기본적으로 script, object, applet, embed, form태그를 처리합니다.

전자정부 프레임워크에서 XSS 방지처리.

    /**
    * XSS 방지 처리.
    * 
    * @param data
    * @return
    */
   protected String unscript(String data) {
       if (data == null || data.trim().equals("")) {
           return "";
       }
       
       String ret = data;
       
       ret = ret.replaceAll("<(S|s)(C|c)(R|r)(I|i)(P|p)(T|t)", "&lt;script");
       ret = ret.replaceAll("</(S|s)(C|c)(R|r)(I|i)(P|p)(T|t)", "&lt;/script");
       
       ret = ret.replaceAll("<(O|o)(B|b)(J|j)(E|e)(C|c)(T|t)", "&lt;object");
       ret = ret.replaceAll("</(O|o)(B|b)(J|j)(E|e)(C|c)(T|t)", "&lt;/object");
       
       ret = ret.replaceAll("<(A|a)(P|p)(P|p)(L|l)(E|e)(T|t)", "&lt;applet");
       ret = ret.replaceAll("</(A|a)(P|p)(P|p)(L|l)(E|e)(T|t)", "&lt;/applet");
       
       ret = ret.replaceAll("<(E|e)(M|m)(B|b)(E|e)(D|d)", "&lt;embed");
       ret = ret.replaceAll("</(E|e)(M|m)(B|b)(E|e)(D|d)", "&lt;embed");
       
       ret = ret.replaceAll("<(F|f)(O|o)(R|r)(M|m)", "&lt;form");
       ret = ret.replaceAll("</(F|f)(O|o)(R|r)(M|m)", "&lt;form");

       return ret;
   }

위와 같은 방식으로 태그들을 제한합니다. XSS가 가장 일어나기 좋은 환경인 게시판의 등록과 수정 부분에서는 DB에 입력할 때, unscript 처리를 해서 저장을 하게 됩니다. (아래 소스)

board.setNttCn(unscript(board.getNttCn()));	// XSS 방지

이렇게 1차적으로 방지를 하고 제가 등록한 글의 불필요한 html 태그가 저장되는 것을 막는 전자정부의 HTMLTagFilter를 이용해서 한번 더 XSS를 방지합니다.

<!-- 전자정부프레임워크 HTMLTagFilter -->
<filter> 
  <filter-name>HTMLTagFilter</filter-name>
  <filter-class>egovframework.com.cmm.filter.HTMLTagFilter</filter-class>
</filter> 
<filter-mapping> 
  <filter-name>HTMLTagFilter</filter-name>
  <url-pattern>*.do</url-pattern>
</filter-mapping>

위에 코드가 web.xml에 추가가 되면 HTMLTagFilter.java에서 *.do로 가는 url들filter처리합니다.

//HTMLTagFilter.java
public class HTMLTagFilter implements Filter{

	@SuppressWarnings("unused")
	private FilterConfig config;

	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {

		chain.doFilter(new HTMLTagFilterRequestWrapper((HttpServletRequest)request), response);
	}

	public void init(FilterConfig config) throws ServletException {
		this.config = config;
	}

	public void destroy() {

	}
}

처음에 구성되어 있는 HTMLTagFilter.java 입니다. 여기에 HTMLTagFilterRequestWrapper 부분에서 XSS가 발생할 수 있는 부분에 대해서 치환처리를 해줍니다. 그리고 Filter는 예외를 지정하는 곳이 따로 없기 때문에 메서드를 따로 구현해야 합니다. 또 다른 방법으로는 HTMLTagFilterRequestWrapperwhiteListTag에 허용할 태그를 등록하는 것이 있습니다. 저의 상황은 에디터 적용시에 보이는 화면상 html 태그들의 예외처리기 때문에 태그가 복잡해져서 equals로 태그를 비교하는 방식인 whiteListTag에 추가하는 방식으로 적용하기 쉽지 않아서 HTMLTagFilter.java에 메소드를 추가를 해주었습니다.

/*
 * Copyright 2008-2009 MOPAS(MINISTRY OF SECURITY AND PUBLIC ADMINISTRATION).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package egovframework.com.cmm.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

public class HTMLTagFilter implements Filter{

	@SuppressWarnings("unused")
	private FilterConfig config;

	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {

		HttpServletRequest req = (HttpServletRequest) request;
		if(checkUrl(req)){ // /admin/로 시작하는 url은 filter를 하지 않는다.(아래의 checkUrl method) 
			chain.doFilter(request, response); 
		}else {
			chain.doFilter(new HTMLTagFilterRequestWrapper((HttpServletRequest)request), response);
		}
	}

	public void init(FilterConfig config) throws ServletException {
		this.config = config;
	}

	public void destroy() {

	}
	// "/admin/"로 시작하는 url인지 확인하는 method 
	private boolean checkUrl(HttpServletRequest req) { 
		String uri = req.getRequestURI().toString().trim(); 
		if(uri.startsWith("/admin/")){
			return true; 
		}else {
			return false; 
		}
        
	}	
    
}

제가 구성하는 페이지는 admin으로 시작하는 url에서 등록을 처리해서 uri.startsWith("/admin/")일 경우 HTMLTagFilterRequestWrapper를 타지 않게 처리를 했습니다.

하루정도 xss처리에 대해서 찾아보니 비슷한 사례를 가지신 분들이 생각보다 많았고 ckeditor상의 문제인지 xss방지처리의 에러인지 헤메이시는 분들이 많은 것 같습니다. 이 글을 참고하시고 질문이나 제가 놓친부분이 있다면 언제든 댓글 부탁드립니다.

좋은 웹페이지 즐겨찾기