티스토리 뷰

개발/자바

JAVA XSS API 소개

jongqui 2024. 4. 13. 00:13

요청을 XSS escape하는 것을 도와주는 오픈소스 API가 몇개 있다. 

 

 

 

 

1. commons-text

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-text</artifactId>
            <version>1.11.0</version>
        </dependency>

 

commons-text의 경우엔 아파치 재단에서 만든 "String 처리"를 위한 API를 모아놓은 artifact이다. 

xsx escape 만을 위한 도구는 아니지만, 여기에도 xss escape를 위한 API가 있다.

 

org.apache.commons.text.StringEscapeUtils#escapeHtml4(String input)

 

이 그 주인공이다. static method이기 때문에 그냥 쓰면 된다. 이 메서드는 우리가 흔히 아는 "<", ">" "\"" , "\'" 등의 문자열을 escape 해서 공격을 막는다.

사실 원래는 Antisamy만 사용하려고 했으나, 아래 문제로 인해 도입하게 되었다.

  • unity rich text(링크)를 입력 받을 수 없다. size 태그와 color 태그를 사용 불가능한 태그로 인식하고, 시원하게 태그를 날려버린다. Antisamy의 정책설정으로 풀어보려고 했으나, 왜인지는 모르겠으나 적용에 실패했다. 
  • Antisamy의 동작 특성상, 아무래도 단순 치환작업보단 리소스를 더 사용할 수 밖에 없다. 경우에 따라서는 부하가 심한 웹앱에도 적용될 가능성이 있다. 따라서 HTML editor에서만 Antisamy를 적용해야 할 수도 있기 때문에 도입한다. 

2. Antisamy

 

        <dependency>
            <groupId>org.owasp.antisamy</groupId>
            <artifactId>antisamy</artifactId>
            <version>1.7.5</version>
        </dependency>

사실 HTML 에디터를 사용하지 않는다고 가정하면, 일반 문자 escape로도 충분할 것이다. 

그러나 HTML 에디터를 사용해야하면 Antisamy가 필요해진다. 

OWASP라는 소프트웨어 보안 재단의 오픈소스 중 하나로, JAVA artifact 형태로 지원해준다.

 

 

OWASP AntiSamy | OWASP Foundation

AntiSamy is a Java component that can sanitize HTML/CSS to eliminate potentially malicious JavaScript.

owasp.org

 

 

이 API는 HTML의 사용성을 훼손하지 않으면서도 XSS 공격을 막는데 초점을 두고 있다.

또한, 기본설정 외에도 사용자가 커스터마이징할 수 있게 XML로 설정할 수 있게 설정 파일도 제공해준다. 

 

사용방법은 매우 간단하다.

        String path = "antisamy.xml"; // 설정 파일 경로. 여기선 spring boot를 사용해서 resources 하위의 경로이다.
        try {
            InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(path);
            Policy policy = Policy.getInstance(inputStream); // policy xml 파일을 객체로 변환.
            AntiSamy antiSamy = new AntiSamy(policy); // 작업할 Antisamy 객체 생성
            CleanResults cleanResults = antiSamy.scan("<span>Hello Kimchi</span><script>alert('XSS')</script>"); //변환작업 
            String result = cleanResults.getCleanHTML(); //변환 
            List<String> errorMessages = cleanResults.getErrorMessages(); //변환 작업 중에 발견해 제거한 이슈 목록
            System.out.println("result = " + result);
            System.out.println("errorMessages = " + errorMessages);
        } catch (PolicyException e) {
            throw new RuntimeException(e);
        } catch (ScanException e) {
            throw new RuntimeException(e);
        }

 

 

참고로 기본설정은 아래처럼 Policy.getInstance()를 사용하면 된다. 

      try {
            Policy policy = Policy.getInstance();
            AntiSamy antiSamy = new AntiSamy(policy);
            CleanResults cleanResults = antiSamy.scan("<span>Hello Kimchi</span><script>alert('XSS')</script>");
            String result = cleanResults.getCleanHTML();
            List<String> errorMessages = cleanResults.getErrorMessages();
            System.out.println("result = " + result);
            System.out.println("errorMessages = " + errorMessages);

        } catch (PolicyException e) {
            throw new RuntimeException(e);
        } catch (ScanException e) {
            throw new RuntimeException(e);
        }

 

 

위 코드를 실행하면, 아래처럼 콘솔에 출력된다. 

result = <span>Hello Kimchi</span>
errorMessages = [The script tag is not allowed for security reasons. This tag should not affect the display of the input. ]

 

apache의 경우엔 단순하게 문제의 문자들을 검출해 치환하는 수준이었다면, 여기는 문제의 tag, attribute 를 검증한다.

기본 설정파일은 antisamy.xml 을 까보면, html tag 별로 어떤 동작을 할지를 정의하고 있다. 

<tag name="script" action="remove" />
<tag name="iframe" action="remove" />
<allowed-empty-tags>
    <literal-list>
        <literal value="br" />
        <literal value="hr" />
        <literal value="a" />
        <literal value="img" />
        <literal value="link" />
        <literal value="iframe" />
        <literal value="script" />
        <literal value="object" />
        <literal value="applet" />
        <literal value="frame" />
        <literal value="base" />
        <literal value="param" />
        <literal value="meta" />
        <literal value="input" />
        <literal value="textarea" />
        <literal value="embed" />
        <literal value="basefont" />
        <literal value="col" />
        <literal value="div" />
    </literal-list>
</allowed-empty-tags>

 

 

설정을 해야할 수도 있다.

예를 들어, 사용자 요구에 따라 iframe을 사용할 수 있어야 했다. 

아무 출처나 적용 가능하면 문제가 될 수 있어 정규표현식으로 출처를 제한한다.

 

common-attributes에 정규표현식을 선언해, 각 attribute의 값이 정규표현식과 일치하는지 검사시킬 수 있다.

iframe의 검사식은 아래와 같이 적용한다. (주석 참고)

<common-attributes>
 <!-- 중략--> 
<regexp name="iframeSrc" value="^https?:\/\/(?:www\.)?youtube\.com\/(.*)$|^\/\/(?:www\.)?youtube\.com\/(.*)$"/> <!-- Youtube 만 허용하는 정규표현식--> 
<!-- 후략--> 
</common-attributes>

 <!-- 중략--> 
 
  <tag-rules>
  <!-- 중략-->
   <tag name="iframe" action="validate"> <!-- iframe 태그는 검사를 해야한다. -->
            <attribute name="src">
                <regexp-list>
                    <regexp name="iframeSrc"/> <!-- Youtube 만 허용하는 정규표현식을 적용한다. -->
                </regexp-list>
            </attribute>
            <attribute name="allow">
                <regexp-list>
                    <regexp name="anything"/>
                </regexp-list>
            </attribute>
            <attribute name="width">
                <regexp-list>
                    <regexp value="(\d+)(px|%)?"/> <!-- 숫자와 px, % 만 허용한다. -->
                    <regexp value="(\d+)"/>
                </regexp-list>
            </attribute>
            <attribute name="height">
                <regexp-list>
                    <regexp value="(\d+)(px|%)?"/> <!-- 숫자와 px, % 만 허용한다. -->
                    <regexp value="(\d+)"/>
                </regexp-list>
            </attribute>
            <attribute name="frameborder"> 
                    <regexp value="(\d+)"/> <!-- 정수만 허용한다. -->
                <regexp-list>
                </regexp-list>
            </attribute>
            <attribute name="allowfullscreen">
                <regexp-list>
                    <regexp value="\s*"/> <!-- 공백허용. -->
                    <regexp name="boolean"/> <!-- boolean 값 허용한다. -->
                </regexp-list>
            </attribute>

        </tag>
  </tag-rules>

 

 

3. lucy 필터

 

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

 

네이버에서 개발한 XSS Escape API이다.

기본적인 동작은 Servlet 스펙의 Filter를 이용해, HttpServletWrapper를 구현해, getParameter 메서드의 문자들을 escape 하는 방식이다. 

유지보수하지 않은지 오래되었기 때문에 jakarta 패키지를 사용하는 최신버전에선 사용이 어려울 것으로 보인다.

또한 json 요청을 처리할 수 없다는 단점이 있다. 

 

 

GitHub - naver/lucy-xss-servlet-filter

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

github.com

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함