1. 소개

XSS(Cross Site Scripting) 공격을 방어하는 Java 라이브러리

  • 화이트 리스트(White List) 설정 방식

    • 블랙리스트 방식에 비해 새로운 공격 유형에 더 안전함

  • 규칙을 선언한 XML 파일 사이의 상속, 오버라이딩 가능

    • 보안 정책을 정하는 부서에서 상위 설정 파일을 제공하고 서비스별로 필요한 정책을 하위 선언 파일에 기술하는 방식으로 사용이 가능함

  • 메모리를 효율적으로 쓰는 SAX 방식의 HTML 파싱 모듈 제공

  • HTML5, HTML4 Transitional DTD 명세 지원

  • 공격 패턴 검출 시 주석문으로 알림

    • <!-- Not Allowed Tag Filtered -→ 주석을 추가해서 허용되지 않는 태그임을 알려줌

  • 기능 확장이 지점 제공 (ElementListener, AttributeListener)

  • Malformed HTML도 파싱 가능

    • EBNF(Extended Backus-Naur Form) Notation을 기반으로 재정의된 파싱 규칙을 사용하여 Malformed HTML도 원본의 변형 없이 파싱함

Note
XSS란?

XSS(cross-site scripting)는 웹페이지에 악의적인 스크립트 코드를 주입할수 있는 취약점이다.

2. Quick Start

Lucy XSS Filter는 Maven Central Repository 에 배포되어 있다. pom.xml 에 아래와 같이 의존성을 선언한다.

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

2.1. XssPreventer

문자열에서 HTML으로 인식될만한 요소를 완전히 제거한다. 예를 들면 <&lt; , >&gt; 등으로 변환한다.

특별한 설정없이 XssPreventer.escape(String) 메서드를 호출한다.

String clean = XssPreventer.escape(dirty);

2.2. XssFilter

HTML 요소를 허용하되 XSS 공격에 위험한 요소를 걸러내야할 때 사용한다. HTML이 허용되는 게시판의 본문 등에 적용할 수 있다.

2.2.1. SAX 방식

아래와 같이 검사 규칙에 대한 설정 파일을 작성한다.

  • lucy-xss-superset-sax.xml 을 이 저장소의 /conf/lucy-xss-superset-sax.xml 을 참조해 작성한다.

    • 특별히 추가할 정책이 없다며 해당 파일을 그대로 써도 좋다.

  • lucy-xss-sax.xml/conf/lucy-xss-sax.xml 참조해 작성한다.

  • 모든 설정 파일을 작성 후 클래스패스의 최상위 경로에 저장한다.

나머지 사용법은 DOM 방식일 때와 동일한다. XssFilter 대신 XssSaxFilter 를 사용한다.

LucyXssFilter filter = XssSaxFilter.getInstance("lucy-xss-sax.xml");
String clean = filter.doFilter(String dirty);

2.2.2. DOM 방식

아래와 같이 검사 규칙에 대한 설정 파일을 작성한다.

  • lucy-xss-superset.xml 을 이 저장소의 /conf/lucy-xss-superset.xml 을 참조해 작성한다.

    • 특별히 추가할 정책이 없다며 해당 파일을 그대로 써도 좋다.

  • lucy-xss.xml/conf/lucy-xss.xml 참조해 작성한다.

  • 모든 설정 파일을 작성 후 클래스패스의 최상위 경로에 저장한다.

XssFilter.getInstance() 메서드로 LucyXssFilter의 인스턴스를 생성한다. 해당 메서드에 입력 파라미터가 없을 경우, lucy-xss-superset.xml 에 정의된 규칙으로 LucyXssFilter 인스턴스를 생성한다. 모든 설정 파일이 없을 경우 jar 파일 안에 내장된 lucy-xss-default.xml 에 정의된 규칙을 읽는다.

LucyXssFilter filter = XssFilter.getInstance();

특정 설정 파일을 명시할때는 XssFilter.getInstance(String) 메서드를 활용한다.

LucyXssFilter filter = XssFilter.getInstance("lucy-xss.xml");

공격패턴 검출시 디버그를 위해 추가되는 주석문을 표시하지 않기 위해서는 다음과 같이 인스턴스를 생성한다.

LucyXssFilter filterComment = XssFilter.getInstance("lucy-xss.xml", true);

문자열 필터링은 아래와 doFilter(String) 메서드를 활용한다.

String clean = filter.doFilter(dirty);

3. XssPreventer

com.nhncorp.lucy.security.xss.XssPreventer 는 모든 HTML 요소를 무력화 시키는 기능을 제공한다. apache-common-lang의 StringEscapeUtils 를 내부적으로 사용하고 있다.

XssPreventer에서 에스케이프하는 문자열은 아래와 같다.

아래와 같이 escape 메서드를 호출해서 HTML의 모든 태그를 무력화시킨 문자열로 변환한다.

String clean = XssPreventer.escape(dirty);

치환된 문자열에서 다시 원본 메시지를 복구하려면 아래와 같이 unescape 메서드를 호출한다.

String origin = XssPreventer.unescape(clean);

4. XssFilter

XSS 공격이 가능한 HTML 요소를 신뢰할 수 있는 코드로 변환하거나 삭제하는 기능을 제공한다. 공격이 가능하지 않은 HTML 요소는 허용을 한다.

4.1. 설정 파일

Lucy-XSS Filter의 필터링 규칙은 화이트리스트 방식으로 설정된다. 화이트리스트 방식은 허용되는 내용을 제외한 모든 부분을 필터링하여 새로운 공격 유형에 대해서도 대처 가능하다. 화이트리스트 설정 파일은 XML 형식으로 작성하며 상위 설정 파일을 상속하거나 오버라이딩 할 수 있다.

화이트리스트 설정 파일의 구조는 다음과 같다.

<config xmlns="http://www.nhncorp.com/lucy-xss" extends="...">
    <elementRule>
    </elementRule>
    <attributeRule>
    </attributeRule>
    <elementGroup name="...">
    </elementGroup>
    <attributeGroup name="...">
    </attributeGroup>
</config>

'config’는 최상위 요소(root element)로서 다음과 같은 속성과 내용을 포함한다.

  • xmlns : 디폴트 네임스페이스

  • extends : 상속 받을 설정 파일 이름(해당 파일 내에 정의된 “config”를 모두 상속받는다.)

  • elementRule : 적용 가능한 모든 요소에 대한 필터링 규칙을 설정한다.

  • attributeRule : 적용 가능한 모든 속성에 대한 필터링 규칙을 설정한다.

  • elementGroup : 요소의 집합을 정의한다. (DOM 방식에서만 사용)

  • attributeGroup : 속성의 집합을 정의한다. (DOM 방식에서만 사용)

  • filteringTagInComment : HTML 주석(<!-- 주석 -→) 내에 존재하는 요소(HTML 태그)에 대한 필터링 여부와 타입을 설정한다.

4.1.1. elementRule

적용 가능한 모든 요소에 대한 필터링 규칙을 정의하며 다음과 같이 작성한다.

<elementRule>
    …
    <element name="a" endTag="true" override="true" disable="false">
        <attributes>
            …
            <ref name="Common" />
            …
        </attributes>
        <elements>
            …
            <ref name="Inline">
                <excludes>
                    <ref name="a" />
                </excludes>
            </ref>
            …
        </elements>
        <listener>com.nhncorp.security.xss.XXXListener</listener>
        …
    </element>
    …
</elementRule>
  • elementRule : 하나 이상의 element 요소로 정의되며, element 필터링 규칙에 없는 요소는 필터링 대상이 된다.

  • element : 하나의 요소에 대한 필터링 규칙을 설정한다.

    • name : 요소 이름

    • endTag : End Tag의 존재 유무를 true/false 로 설정한다. 디폴트는 false 이다. (DOM 방식에서만 사용)

    • override : 'extends' 속성에 기술한 상위 설정 파일에 동일한 설정이 있으면 그 설정을 상속받을지를 설정한다. 디폴트는 `true`이다.

    • disable : name 에 설정된 요소를 elementRule 에서 제거할지 설정한다. 디폴트는 false 이다.

    • removeTag : 필터링 결과물에서 element의 삭제 유무를 true/false 로 설정한다. 디폴트는 false 이다. 가령 <body>의 내용만 사용되는 경우, <html>, <head>, <body> 등을 삭제하는데 사용될 수 있다.

  • attributes : element의 속성을 입력한다. (DOM 방식에서만 사용)

  • elements : element의 하위 요소를 입력한다. (DOM 방식에서만 사용)

  • listener : 이 화이트리스트 설정 파일의 설정만으로 해당 요소를 필터링할 수 없는 경우, com.nhncorp.lucy.security.xss.event.ElementListener 인터페이스를 상속하여 구현한 클래스 이름을 입력한다.

  • ref : elementRule 또는 attributeRule에 설정된 element/attribute에 대한 레퍼런스이다. elementGroup 또는 attributeGroup을 레퍼런스할 수도 있다.

  • name : 레퍼런스 이름.

  • excludes : ref가 elementGroup 또는 attributeGroup의 레퍼런스인 경우 지정한 ref를 제외시킨다. (DOM 방식에서만 사용)

4.1.2. attributeRule

적용 가능한 모든 속성에 대한 필터링 규칙을 정의하며, 다음과 같이 작성한다.

<attributeRule>
    …
    <attribute name="class" override="true" disable="false">
        <allowedPattern><![CDATA[…..]]></allowedPattern>
    </attribute>
    <attribute name="class" override="true" disable="false" base64Decoding="true">
        <notAllowedPattern><![CDATA[…..]]></allowedPattern>
    </attribute>
    …
</attributeRule>
  • attributeRule : 하나 이상의 하위 attribute 태그를 가지는 부모 태그이다.

  • attribute : 하나의 속성에 대한 필터링 규칙을 설정한다.

    • name : 속성 이름

    • override : config의 'extends' 속성에 설정한 상위 설정 파일에 동일한 설정이 있으면 그 설정을 상속받을지를 결정한다. 디폴트는 true 이다.

    • disable : name에 설정된 속성을 attributeRule에서 제거할지 설정한다. 디폴트는 false 이다.

    • Base64Decoding : 속성 값에 필터링 규칙을 적용하기 전에 base64 디코딩 수행 여부를 설정한다. 디폴트는 false 이다.

    • exceptionTagList : attribute가 disable 설정이지만, 특정 태그에서는 해당 attribute 사용을 허용 하고 싶을 경우 사용한다. 가령, <attribute name="class" disable="true" /> 설정이 되어 있을 경우, 모든 태그에서 class 속성이 허용되지 않는다. 즉 기존에는 table 태그에서만 class 속성을 허용하고 싶을 경우 방법이 없었다. 하지만 이제는 아래와 같이 exceptionTagList 속성을 사용하면 table에서 class 속성을 사용할 수 있도록 예외처리가 가능하다.

  • allowedPattern : 속성 값으로 허용되는 정규 표현식(regular expression)을 입력한다. 패턴에 해당되지 않는 모든 속성 값은 필터링 된다.

  • notAllowedPattern : 속성 값으로 허용되지 않는 정규 표현식을 입력한다. 패턴에 해당되는 모든 속성 값은 필터링 된다. attributeRule에 notAllowedPattern과 allowedPattern이 동시에 정의되어있을 경우 notAllowedPattern을 기본 적용 후 allowedPattern으로 예외처리를 할 수 있다. <br> 예를 들어 현재 href 속성 값에는 디폴트 보안설정(lucy-xss-superset.xml 또는 lucy-xss-superset-sax.xml)에 따라 javascript: 패턴이 올 수 없다. <br> 하지만 서비스 응용에 따라 특정 javascript 메소드를 허용하고자 할 때, 해당 메소드를 allowedPattern으로 추가하면 된다.

4.1.3. elementGroup/attributeGroup(DOM 방식에서만 사용)

elementGroup과 attributeGroup은 각각 elementRule과 attributeRule에 설정된 element/attribute에 대한 레퍼런스의 집합을 그루핑하는 역할을 담당한다. 다음과 같이 작성한다.

<elementGroup name="Inline">
    …
    <ref name="a" />
    …
</elementGroup>

<attributeGroup name="Core">
    …
    <ref name="class" />
    …
</attributeGroup>
  • elementGroup/attributeGroup : elementRule과 attributeRule에 설정된 element/attribute들에 대한 레퍼런스의 집합을 그루핑한다.

    • name : 그룹 이름

    • override : config의 'extends' 속성에 기술한 상위 설정 파일에 동일한 설정이 있으면 그 설정을 상속받을지 설정한다. 디폴트는 true 이다.

  • ref : elementRule 또는 attributeRule에 기술된 element/attribute에 대한 레퍼런스이다. 다른 elementGroup/attributeGroup을 참조할 수도 있다.

4.1.4. IEHackExtension(IE핵 태그)

Lucy-XSS Filter 에서는 IE핵을 주석이 아닌 별도의 태그로 인식하며, 어떤 태그에도 올 수 있도록 허용 하고 있다. 혹 IE 핵 태그를 삭제(비 허용)할 필요가 있다면, IE 핵 태그를 disable 시키도록 설정하면 된다. 기본적으로 IE핵 태그의 자식 컨텐츠는 삭제되지 않는다. 자식 컨텐츠를 삭제하고 싶다면 별도의 ElementListener를 구현하거나 Xss Filter에서 기본적으로 제공하는 ContentsRemoveListener를 IE 핵 태그에 리스너를 아래와 같이 설정하면 된다. ContentsRemoveListener는 XssFilter 객체일 경우에만 사용 가능하다. XssSaxFilter에서는 제대로 동작하지 않는다.

…
<element name="IEHackExtension" disable=”true”>
	   <listener>com.nhncorp.lucy.security.xss.listener.ContentsRemoveListener</listener>
</element>
…

IEHackExtension은 Element를 상속 받은 클래스로서 Element에서 사용하는 속성 및 메소드 모두 사용 가능하다.

4.1.5. filteringTagInComment

Lucy-XSS Filter에서는 HTML 주석(<!-- 주석문 -→)내에 존재하는 HTML 태그에 대해서 XSS 필터링 여부를 설정하고 타입을 지정할 수 있다. 설정을 명시하지 않은 경우, 주석 내에 존재하는 괄호 < , >&lt; , &gt; 으로 변환하는 것을 디폴트로 한다. 주석 내 필터링을 통해 검출된 공격 패턴에 대해서는 동작하지 않도록 필터링할 뿐, 주석 설명은 제공하지 않는다.

…
<!-- // type1 : 가장 보안 수준이 높은 타입, 모든 태그를 필터링한다. (디폴트 타입, 권장) // -->
<filteringTagInComment enable="true" type="strict"/>

<!-- // type2 : 주석 밖의 태그에 적용되는 필터링 규칙과 동일한 규칙을 사용해 태그를 필터링 한다.// -->
<filteringTagInComment enable="true" type="config"/>

<!-- // type3 : 주석 내에 존재하는 태그를 그대로 보존한다. (비권장)// -->
<filteringTagInComment enable="false"/>
…

4.1.6. 테스트

규칙이 의도대로 설정되었는지는 테스트 코드로 반드시 검증해 본다.

XssFilter filter = XssFilter.getInstance("lucy-xss-superset.xml");

@Test
public void testDirtyCodeFiltering() throws Exception {
    String dirty = "<script></script>";
    String clean = filter.doFilter(dirty);
    String expected = "&lt;script&gt;&lt;/script&gt;"; // 예상 문자열
    assertEquals(expecte, clean);
}

4.2. XssSaxFilter

기본적으로 제공되는 SAX 방식용 화이트리스트 설정 파일은 다음과 같다.

  • lucy-xss-default-sax.xml

    • HTML 4.0을 기본으로 하며, HTML 5.0과 Internet Explorer에 특화된 몇 가지 명세를 추가한 기본 설정 파일이다.

    • lucy-xss-x.x.x.jar 파일 내부에 포함되어 배포된다.

  • lucy-xss-superset-sax.xml :

    • lucy-xss-default-sax.xml을 상속받아 작성된 파일이다. 회사의 보안부서에서 기초 정책을 저으이하고 있다.

    • 이 저장소의 conf 디렉토리 아래에서 배포된다.

XssSaxFilter.getInstance() 메서드로 설정 파일을 지정하지 않은 경우에는 lucy-xss-superset-sax.xml 파일을 클래스패스에서 찾아 읽는다. lucy-xss-superset-sax.xml 파일이 존재하지 않으면, jar 파일에 내장된 lucy-xss-default-sax.xml 을 읽어 들인다.

XssSaxFilter filter = XssSaxFilter.getInstance();

lucy-xss-superset-sax.xml 을 명시적으로 지정하여 인스턴스를 생성할 수도 있다.

XssSaxFilter otherFilter = XssSaxFilter.getInstance("lucy-xss-superset-sax.xml");

서비스별로 특수한 정책을 설정해야한다면 다음과 같이 설정파일을 작성한다.

lucy-xss-superset-sax.xml 을 상속받도록 extends 속성을 추가한다.

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

</config>

애플리케이션의 성격에 따라서 상위 설정 파일에서 정의한 규칙을 재정의하거나 새로운 규칙을 추가한다.

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

    <attributeRule>
    </attributeRule>
</config>

입력 파라미터로 직접 작성한 XSS 설정파일("lucy-xss-sax.xml")을 읽어 들여 인스턴스를 생성한다.

XssSaxFilter otherFilter = XssSaxFilter.getInstance("lucy-xss-sax.xml");

공격패턴 검출시 디버그를 위해 추가되는 주석문을 표시하지 않기 위해서는 다음과 같이 인스턴스를 생성한다.

XssSaxFilter filterNoComment = XssSaxFilter.getInstance(true);
XssSaxFilter otherFilterNoComment = XssSaxFilter.getInstance("lucy-xss-sax.xml", true);

doFilter() 메서드로 필터링을 수행한다.

String clean = filter.doFilter(String dirty);

주요 메서드들은 다음과 같다.

  • static XssSaxFilter getInstance() : 이 메소드는 XSS 코드가 포함된 HTML 의 모든 태그를 신뢰할 수 있는 문자열로 변환해 반환한다

  • static XssSaxFilter getInstance(boolean withoutComment) : 이 메소드는 escape하기 전의 문자열로 변환해 리턴한다.

  • static XssSaxFilter getInstance(String fileName) : 이 메소드는 입력한 파일 이름에 해당하는 설정 파일을 사용하여 생성한 Lucy-XSS Filter 객체를 반환한다. 해당 파일을 찾을 수 없는 경우, 예외(exception)를 발생시킨다. 공격 패턴 검출 시 추가되는 주석문을 제거하는 인스턴스 변수는 디폴트로 false 값이 지정되어 주석문이 표시된다

  • static XssSaxFilter getInstance(String filename, boolean withoutComment) : getInstance(String fileName) 메소드와 동일하게 동작한다. 단, 공격 패턴 검출 시 추가되는 주석문 삭제를 나타내는 인스턴스 변수값이 withoutComment 파라미터 값에 의해 결정된다. true 이면 주석문을 표시하지 않는다

  • String doFilter(String dirty) : 이 메소드는 XSS 코드가 포함된 HTML 문자열을 신뢰할 수 있는 코드로 변환하거나 삭제한 후 결과물을 인자로 받은 writer객체에 write한다. 즉 외부에서 writer를 제어할 수 있는 인터페이스를 제공한다

  • String doFilter(char[] dirty, int offset, int count, Writer writer) : 이 메소드는 XSS 코드가 포함된 HTML 문자열을 char[]로 받아 신뢰할 수 있는 코드로 변환하거나 삭제한 후 결과물을 인자로 받은 writer객체에 write한다. 즉 외부에서 writer를 제어할 수 있는 인터페이스를 제공한다.

  • String doFilter(String tagName, String attName, String dirtyAttValue) : 이 메소드는 특정 HTML 요소 내의 속성 값으로 삽입되는 XSS 코드를 신뢰할 수 있는 코드로 변환하거나 삭제한다.

XssSaxFilter는 아래와 같은 규칙으로 필터링을 한다.

  • 닫는 태그를 검사하지 않는다(닫는 태그가 없다고 공격 태그로 간주하지는 않는다).

  • 부모 자식 관계는 검사하지 않는다(ex) DIV 태그가 어느 태그에도 올 수 있다).

  • 태그와 속성 관계 또한 검사하지 않는다(속성의 사용가능 여부, 속성 값의 XSS 공격 검사는 여전히 한다).

  • start tag 없이 end tag만 있을 경우, 필터링 없이 그대로 노출한다.

    • 예) 필터링 전: </div>, 필터링 후: </div>

  • 기존 XssFilter는 </div> 만 있을 경우 필터링 처리되어 &lt;/div&gt; 가 출력된다.

  • 태그, 속성들의 포함관계를 검사하지 않기 때문에, 서비스 입장에서는 특정 위치에서의 태그 사용을 위해 필요했던 추가적인 설정들이 줄어든다.

4.2.1. 성능과 메모리 사용량

XssSaxFilter는 DOM 트리 생성 과정 및 부모 자식 간의 검사가 생략되어 DOM 방식에 비해 속도가 약 20% 빨라졌다. 특히 중첩된 태그가 많은 인풋일 수록 SAX 방식에서 큰 속도 향상이 있다.

DOM 파서 방식의 XssFilter를 사용할 때는 입력된 문자열의 크기 대비 8배의 메모리를 사용하였으나,XssSaxFilter는 약 3배 사용한다. (출력 문자열 + 입력 무자열의 char[]배열 + 기타 설정용 객체)

Java에서 String 클래스는 내부적으로 문자열을 저장하기 위하여 char 형의 배열을 사용한다. 이때 char 형은 자바의 문자 인코딩 방식이 유니코드이므로 1바이트가 아니라 2바이트의 크기를 가진다. 따라서 XssFilter의 대상 문자열을 파일에서 읽을 경우, 파일 용량의 약 2배 이상의 메모리가 필요하다는 점을 염두에 두어야한다.

메모리에 민감한 서비스에서는 XssFilter 객체 생성시 getInstance(java.lang.String filename, boolean withoutComment) 메소드의 withoutComment 인자로 true 를 넘기길 권장한다. 즉 공격 패턴 검출 시 추가되는 주석문 생성 옵션을 끌 경우 메모리 사용량을 약 10% 줄일 수 있다.

XssFilter filter = XssFilter.getInstance("lucy-xss-superset.xml", true);

4.3. XssFilter (DOM 방식)

DOM 방식의 XssFilter를 위해 기본 제공되는 화이트리스트 설정 파일은 아래와 같다.

  • lucy-xss-default.xml

    • HTML 4.0을 기본으로 하며, HTML 5.0과 Internet Explorer에 특화된 몇 가지 명세를 추가한 기본 설정 파일이다.

    • lucy-xss-x.x.x.jar 파일 내부에 포함되어 배포된다.

  • lucy-xss-superset.xml :

    • lucy-xss-default.xml을 상속받아 작성된 파일이다. 회사의 보안부서에서 기초 정책을 저으이하고 있다.

    • 이 저장소의 conf 디렉토리 아래에서 배포된다.

XssFilter.getInstance() 메서드로 설정 파일을 지정하지 않은 경우에는 lucy-xss-superset.xml 파일을 클래스패스에서 찾아 읽는다. lucy-xss-superset.xml 파일이 존재하지 않으면, jar 파일에 내장된 lucy-xss-default.xml 을 읽어 들인다.

XssFilter filter = XssFilter.getInstance();

서비스별로 특수한 정책을 설정해야한다면 다음과 같이 설정파일을 작성한다.

lucy-xss-superset.xml 을 상속받도록 extends 속성을 추가한다.

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

</config>

애플리케이션의 성격에 따라서 상위 설정 파일에서 정의한 규칙을 재정의하거나 새로운 규칙을 추가한다.

<config xmlns="http://www.nhncorp.com/lucy-xss" extends="lucy-xss-superset.xml">
    <elementRule>
    </elementRule>
    <attributeRule>
    </attributeRule>

    <elementGroup name="…">
    </elementGroup>
    <attributeGroup name="…">
    </attributeGroup>
</config>

입력 파라미터로 지정한 XSS 설정파일("lucy-xss.xml")을 읽어 들여 인스턴스를 생성한다.

XssFilter otherFilter = XssFilter.getInstance("lucy-xss.xml");

공격 패턴 검출 시 디버그를 위해 추가되는 주석문을 표시하지 않기 위해서는 다음과 같이 인스턴스를 생성한다.

XssFilter filterNoComment = XssFilter.getInstance(true);

또는

XssFilter otherFilterNoComment = XssFilter.getInstance("lucy-xss.xml", true);

doFilter() 메서드로 필터링을 수행한다.

String clean = filter.doFilter(String dirty);

주요 메서드들은 다음과 같다.

  • static XssFilter getInstance() : “lucy-xss-superset.xml”을 기본 설정 파일로 사용하며, “lucy-xss-superset.xml”이 루트 클래스패스에 없으면 XSS Filter 라이브러리(lucy-xss-1.x.x.jar) 내에 있는 “lucy-xss-default.xml”의 설정 내용을 사용한다. 공격 패턴 검출 시 추가되는 주석문을 제거하는 인스턴스 변수(withoutComment)는 디폴트로 false 값이 지정되고 공격 패턴 검출 시 주석문이 표시된다.

  • static XssFilter getInstance(boolean withoutComment) : getInstance() 메소드와 동일하게 동작한다. 단, 공격 패턴 검출 시 추가되는 주석문 삭제를 나타내는 인스턴스 변수값이 withoutComment 파라미터 값에 의해 결정된다. true 이면 주석문을 표시하지 않는다. 공격 패턴 검출 시 추가되는 주석문 <br> 1. 허용되지 않은 태그 사용 시 <!-- Not Allowed Tag Filtered -→ <br> 2. 허용되지 않은 속성 사용 시 <!-- Not Allowed Attribute Filtered -→ <br> 3. 삭제 설정 된 태그 사용 시 <!-- Removed Tag Filtered ( 삭제 된 태그 ) -→|

  • static XssFilter getInstance(String fileName) : 이 메소드는 입력한 파일 이름에 해당하는 설정 파일을 사용하여 생성한 Lucy-XSS Filter 객체를 반환한다. 해당 파일을 찾을 수 없는 경우, 예외(exception)를 발생시킨다. 공격 패턴 검출 시 추가되는 주석문을 제거하는 인스턴스 변수는 디폴트로 false 값이 지정되어 주석문이 표시된다.|

  • static XssFilter getInstance(String filename, boolean withoutComment) : getInstance(java.lang.String fileName) 메소드와 동일하게 동작한다. 단, 공격 패턴 검출 시 추가되는 주석문 삭제를 나타내는 인스턴스 변수값이 withoutComment 파라미터 값에 의해 결정된다. true 이면 주석문을 표시하지 않는다.|

  • String doFilter(String dirty) : 이 메소드는 XSS 코드가 포함된 HTML 문자열을 신뢰할 수 있는 코드로 변환하거나 삭제한 후 결과물을 String으로 리턴한다.

  • String doFilter(String dirty, Writer writer) : 이 메소드는 XSS 코드가 포함된 HTML 문자열을 신뢰할 수 있는 코드로 변환하거나 삭제한 후 결과물을 인자로 받은 writer객체에 write한다. 즉 외부에서 writer를 제어할 수 있는 인터페이스를 제공한다.

  • String doFilter(String tagName, String attName, String dirtyAttValue) : 이 메소드는 특정 HTML 요소 내의 속성 값으로 삽입되는 XSS 코드를 신뢰할 수 있는 코드로 변환하거나 삭제한다.|

메모리에 민감한 서비스에서는 XssFilter 객체 생성시 getInstance(String filename, boolean withoutComment)메소드의 withoutComment 인자로 true 를 넘기길 권장한다. 공격 패턴 검출 시 추가되는 주석문 생성 옵션을 끌 경우 메모리 사용량을 약 5% 줄일 수 있다. 사용

XssFilter filter = XssFilter.getInstance(“lucy-xss-superset.xml”, true);

4.4. Listener

확장 지점으로 ElementListenerAttributeListener 를 제공한다.

XssSaxFilter를 쓸 때에는 아래와 같은 제약이 있다.

  • Element의 자식을 추가, 삭제, 수정하는 작업을 Listener에서 할 수 없다

    • ContentsRemoveListener, ObjectListener 사용 불가(ParamListener로 대체)).

  • IEHack의 관련한 스펙

    • "IEHack의 startTag만 있고, endTag는 없는 경우나, 또는 endTag가 깨broken인 경우 IEHack 자체(startTag)를 제거" 스펙을 지원하지 않음.

4.4.1. ElementListener

com.nhncorp.lucy.security.xss.event.ElementListener 를 활용하면 특정 요소에 하위 요소를 추가하거나 데이터를 변경할수 있다.

예를 들어, <embed/> 태그에 autostart='falase' 선언과 <param> 요소를 내부에 추가하는 로직을 구현한다고 가정한다. 다음과 같이 ElementListener 인터페이스를 구현하는 클래스를 작성한다.

public class EmbedListener implements ElementListener {
    public void handleElement(Element elem) {
        // autostart="false" 추가
        elem.putAttribute("autostart", "\"false\"");

        // <param> 요소 추가
        elem.addContent(new Element("param"));
    }
}

구현한 클래스는 다음과 같이 XML 파일 안에서 지정한다.

<element name="embed">
    <!-- ElementListener 인터페이스를 구현한 클래스 이름을 기술한다. -->
    <listener>com.nhncorp.lucy.security.xss.test.EmbedListener</listener>
</element>
ElementListener 기본 구현체

Lucy-XSS Filter에서는 <embed/><object/> 요소의 취약점에 대응할 수 있는 ElementListener 구현체를 제공한다. 기본 구현체에는 Flash, Media Player 관련 보안 대응과 소스의 MIME 타입을 확인해서 html일 경우 이스케이프 처리하는 로직이 존재한다.

EmbedListener와 ObjectListener의 로직

추가로 허용하는 도메인인지 확인하기 위해 white-url.xml 에 기술되지 않은 리소스(URL)일 경우 type 속성을 확인하고 허용되지 않은 type(예를 들어 text/html)일 경우 이스케이프 처리한다. Type 속성이 명시되어 있지 않을 경우 리소스의 확장자를 기반으로 이를 유추하고 type 속성을 추가해 준다. 검사하는 MIME 타입은 Apache Httpd에서 지원하는 MIME 타입을 바탕으로 만들어져 있으며 “text/*” 타입만을 제거하였다. Type 속성과 리소스의 확장자 모두 존재하지 않는 경우는 서비스의 가용성을 위해 이스케이프 하지 않는다. Xss filter에서는 허용할 MIME 타입을 추가할 수 있는 확장 지점을 제공한다.

  • 확장포인트(프로퍼티파일 추가) 사용방법

    • 프로젝트의 클래스패스에 xssfilter-extension.properties 파일생성

    • 확장자=MIME TYPE 형식으로 허용 할 확장자와 확장자에 해당하는 MIME TYPE 추가

예를 들어, 기본 정책으로는 막혀있는 확장자인 html을 허용하고자 한다면 아래와 같이 설정하면 된다.

xssfilter-extension.properties 파일
html=text/html
Warning
프로퍼티 파일 이름이 위와 다르거나 MIME 타입 선언이 빠지면 해당 기능이 동작하지 않는다.
Table 1. ObjectListener/EmbedListener/ParamListener 필터링 규칙
항목 필터링 규칙

ObjectListener

(DOM 방식에서만 지원)

아래와 같이 파라미터를 변경하거나, 없는 경우 추가한다.

<param name="allowScriptAccess" value="never" />
<param name="autostart" value="false" />
<param name="invokeURLs" value="false" />
<param name="autoplay" value="false" />
<param name="enablehref" value="false" />
<param name="enablejavascript" value="false" />
<param name="nojava" value="true" />
<param name="AllowHtmlPopupwindow" value="false">
<param name="enableHtmlAccess" value="false">
<param name="allowNetworking" value="internal">

white-url.xml 에 기술된 리소스일 경우 'allowNetworking’을 "all"로, 기술되지 않은 리소스일 경우 "internal"로 변경한다.

EmbedListener

'invokeURLs', 'autostart' 값을 "false"로, 'allowScriptAccess' 값을 "never"로 변경한다.

white-url.xml에 기술된 리소스일 경우 'allowNetworking’을 "all"로, 기술되지 않은 리소스일 경우 "internal"로 변경한다

ParamListener (SAX 방식에서만 지원)

아래와 같이 각 파라미터의 value를 지정된 값으로 변경한다.

<param name="allowScriptAccess" value="never" />
<param name="autostart" value="false" />
<param name="invokeURLs" value="false" />
<param name="autoplay" value="false" />
<param name="enablehref" value="false" />
<param name="enablejavascript" value="false" />
<param name="nojava" value="true" />
<param name="AllowHtmlPopupwindow" value="false">
<param name="enableHtmlAccess" value="false">

param의 부모 태그인 object에 대한 처리는 XssSaxFilter 자체적인 로직에서 처리한다. 결국 SAX 방식에서도 param 태그에 대해 ParamListener를 사용하면, 기존 ObjectListener와 동일한 효과가 있다.

Object, Embed 태그는 디폴트로 제공되는 lucy-xss-superset.xml 설정 파일에는 disable 처리가 되어 있다. 서비스에 따라서는 이 태그들을 사용할 필요가 있는데, 이럴 경우 보안설정이 추가된 ObjectListener / EmbedListener / ParamListener 를 사용해서 XSS 공격을 방어해야 한다. 그렇게 사용할 경우에는 lucy-xss-superset.xml 을 상속하여, 아래와 같이 설정 파일에 기술한다.

<elementRule>
    <element name="embed" disable="false">
        <listener>com.nhncorp.lucy.security.xss.listener.EmbedListener</listener>
    </element>
    <element name="object" disable="false">
        <listener>com.nhncorp.lucy.security.xss.listener.ObjectListener</listener>
    </element>
</elementRule>

XssSaxFilter를 사용하는 경우 lucy-xss-superset-sax.xml 을 상속하여, 아래와 같이 설정 파일에 기술한다.

...
<elementRule>
    <element name="embed" disable="false">
        <listener>com.nhncorp.lucy.security.xss.listener.EmbedListener</listener>
    </element>
    <element name="object" disable="false"></element>
    <element name="param" disable="false">
        <listener>com.nhncorp.lucy.security.xss.listener.ParamListener</listener>
    </element>
</elementRule>
...

ObjectListenerEmbedListener , ParamListenerwhite-url.xml 에 설정된 내용을 바탕으로 필터링을 수행한다. `white-url.xml`은 클래스패스의 최상위 경로에 존재해야 한다. 이 파일이 해당 경로에 없으면 모든 URL 리소스가 필터링 대상이 되므로, 반드시 파일을 작성해야 한다.

white-url.xml 예시
<?xml version="1.0" encoding="UTF-8"?>
<white-url>
    <domain name="http://www.naver.com" desc="네이버">
        <pattern>http://serviceapi.nmv.naver.com/*</pattern>
        <pattern>http://scrap.ad.naver.com/*</pattern>
    </domain>
    <domain name="http://www.daum.net" desc="다음">
        <pattern>http://flvs.daum.net/flvPlayer.swf*</pattern>
    </domain>

    ...
</white-url>

4.4.2. AttributeListener

com.nhncorp.lucy.security.xss.event.AttributeListener 를 활용하면 모든 요소들의 특정 속성에 추가적인 작업을 수행할 수 있다.

예를 들어, src 속성을 가지는 모든 요소들에 대해 허락되지 않은 URL은 빈 문자열로 치환하는 처리를 한다고 가정한다. 아래와 같이 AttributeListener 를 구현한 SrcAttributeListener 를 작성한다.

WhiteUrlList list = WhiteUrlList.getInstance();

public void handleAttribute(Attribute attr) {
    if (!list.contains(attr.getValue())) {
        attr.setValue("\"\"");
    }
}

다음과 같이 XML 설정으로 SrcAttributeListener 를 추가한다.

<attributeRule>
    <attribute name="src">
        <listener>com.nhncorp.lucy.security.xss.listener.SrcAttributeListener</listener>
    </attribute>
</attributeRule>

4.5. 내부 구조

Lucy-XSS Filter는 다음과 같은 요소로 구성된다

  • Lucy-XSS Filter Core : Markup Parser를 통해 파싱한 HTML 문자열을 White List Object Model과 비교하여 필터링한 결과(Clean HTML)를 반환한다.

  • Markup Parser : 문자열을 파싱하여 HTML Object Model로 변환한다. DOM(MarkupParser.java)과 SAX(MarkupSaxParser.java) 두 가지 방식이 존재한다.

  • Configuration Builder : White List Configuration에 정의된 내용을 바탕으로 White List Object Model을 생성한다.

  • White List Configuration : 정상적인 HTML 구조를 화이트리스트 방식으로 정의한 설정 파일로서, XML 형식으로 되어 있다.

내부 모듈 구조

Lucy-XSS Filter 객체를 생성하면 Configuration Builder는 White List Configuration에 정의된 내용을 바탕으로 White List Object Model을 생성하여 Lucy-XSS Filter Core로 전달한다. Lucy-XSS Filter Core는 Markup Parser(DOM, SAX 둘 다 지원 )가 필터링 대상 HTML 문자열을 파싱하여 생성한 HTML Object Model을 White List Object Model과 비교하여 필터링한다.