웹서비스에서는 사용자가 입력한 값을 DB에 저장하거나 가공하거나 화면에 출력해야 하는 일이 많이 일어난다. 특히 이 값을 HTML 형태로 화면에 출력할 때 세심하게 신경쓰지 않으면 화면이 의도한대로 나오지 않거나 XSS 공격이 가능해지는 등의 여러가지 부작용을 초래할 수 있다. 개발단계에서 잘 처리했다고 하더라도 미처 고려하지 못한 케이스가 있을 수 있으니 테스트 단계에서 이런 문제를 발견하는게 중요한데 이런 과정에서 유용하게 사용할 수 있는 패턴을 설명한다.

이 내용은 겐도사마의 재림 :: G-Test Pattern 에서 소개하는 패턴을 응용하여 몇가지 케이스를 추가한 것이다.

패턴

<!--'"&lt;\n+#]]>흫흣</script>

예를 들어 어떤 서비스의 닉네임으로 위의 값을 입력하고 저장했을 때 이 문자열이 그대로 화면에 출력되어야 한다. 한 글자라도 빠지거나 화면이 깨져보인다면 어딘가 잘못됐다고 생각할 수 있다.

각 부분 설명

<!--

HTML 주석의 시작 부분을 의미하는 이 문자열이 아래와 같이 자바스크립트 문자열 안에 포함되어 있다고 가정하자.

<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
	<title>String Test</title>
	<script type="text/javascript">
		var string = 'HTML comment starts with <!-- symbol.';
	</script>
</head>
<body>
	<h1>Test</h1>
	<!-- Here is content -->
	<p>Hello!</p>
</body>
</html>

이 문서를 Internet Explorer에서 열어보면 화면에 아무 것도 보이지 않는다. 정확한 원인은 알 수 없지만 6번째 줄의 <!-- 태그와 11번째 줄의 --> 태그가 어떤 작용을 일으켜 전체 문서 구조를 망가뜨리는 것으로 생각된다 (둘 중 하나를 제거하면 문제가 사라진다). 단순히 <!-- ... --> 부분을 날려버린다고 생각하기에는 <p>Hello!</p> 부분이 출력되지 않는 현상을 설명하기 어렵다.

Internet Explorer 10 버전부터는 위의 문제가 재현되지 않는다.

'"

작은 따옴표와 큰 따옴표의 경우 자바스크립트 문자열 안에 표현될 때 문자열을 어떤 따옴표로 감싸는지를 고려해 앞에 역슬래시를 붙여 이스케이핑 해주면 된다. 보통 서버사이드 코드에서 자바스크립트를  생성해 출력하게 되는데 자바스크립트에서 문자열을 묶는 따옴표를 항상 일정하게 사용해야 서버사이드에서 올바른 이스케이핑 방법을 사용해 출력할 수 있을 것이다. 예를 들어 자바스크립트에서 문자열을 항상 작은 따옴표로 감싸 표현한다면 서버사이드 코드에서는 작은 따옴표 앞에만 역슬래시를 붙여 출력하도록 하면 된다.

HTML 본문에 표시할 때 큰 따옴표는 그냥 출력해도 잘 나오지만 &quot; 엔티티로 인코딩 해서 출력하는게 표준에 맞는 방법이다. 작은 따옴표의 경우도 &apos; 같은 식으로 인코딩할 수 있지만 반드시 그렇게 할 필요는 없다.

&

앰퍼샌드 문자는 HTML 엔티티를 표현하기 위한 접두어로 사용되기 때문에 HTML 본문에서 그냥 출력하면 안되고 항상 &amp;로 출력해야 한다. 또 URL에 표현될 때는 파라미터간의 구분자로 사용되기 때문에 %26로 표현되어야 한다.

http://example.com/?param=ab&cd

위와 같은 경우 param=ab&cd= 처럼 인식되어 param의 값은 'ab'가 된다.

\n

줄바꿈 문자로 사용되는 패턴이다. 자바스크립트 문자열 안에 표현될 때 앞에 역슬래시를 하나 더 붙여주지 않으면 \n이 아니라 줄바꿈으로 인식된다.

+

URL의 쿼리 파라미터의 값으로 이 패턴을 넘길 때 + 문자를 %2B로 인코딩 해주지 않으면 공백으로 인식될 수 있다.

http://example.com/?param=ab+cd

위와 같은 경우 +는 공백으로 인식되어 param의 값은 'ab cd'가 된다.

#

URL의 쿼리 파라미터의 값으로 이 패턴을 넘길 때 # 문자를 %23으로 인코딩 해주지 않으면 # 이후의 값들이 Fragment 지시자로 인식되어버린다.

http://example.com/?param=ab#cd

위와 같은 경우 cd 는 Fragment로 인식되어 param의 값은 'ab'가 된다.

]]>

XML CDATA 노드 안에서는 CDATA를 닫는 ]]> 기호를 표현할 수 없다. 이 때는 CDATA를 사용하지 않거나 ]]> 기호를 두 부분 이상으로 쪼개서 여러개의 CDATA 노드로 표현하는 수 밖에는 없다.

잘못된 출력
<node>
<![CDATA[ A CDATA section starts with "<![CDATA[" and ends with "]]>" ]]>
</node>

위의 경우 처럼 아무런 처리 없이 출력하면 XML이 깨지게 된다.

CDATA를 사용하지 않고 인코딩 해서 출력
<node>
A CDATA section starts with &quot;&lt;![CDATA[&quot; and ends with &quot;]]&gt;&quot;
</node>

사용자가 입력한 값의 경우 처럼 예측할 수 없는 값을 출력해야 할 경우는 위와 같이 CDATA를 사용하지 않고 인코딩 해서 출력하는게 바람직하다. 데이터의 용량이 증가한다는 단점이 있지만 가장 확실한 해결책이다.

CDATA 노드를 두개로 나눠서 표현
<node>
<![CDATA[ A CDATA section starts with "<![CDATA[" and ends with "]]]]><![CDATA[>" ]]>
</node>

CDATA를 꼭 사용해야 한다면 ]]> 문자열을 ]]]]><![CDATA[> 처럼 변환해서 출력할 수 있다. 이렇게 되면 CDATA 노드의 수가 변하기 때문에 XML 노드를 탐색해서 처리해야 하는 경우 주의할 필요가 있다.

흫흣

'' 문자는 EUC-KR 문자셋에서 표현할 수 없는 글자다. UTF-8 환경이 아닌 웹서비스에서도 이 문자를 입력했을 때 오류 없이 처리될 수 있도록 해야 한다.

</script>

자바스크립트 문자열 안에서 </script> 문자열이 표현될 때 / 문자 앞에 역슬래시를 넣어주어야 정상적인 문자열로 인식된다. 그렇지 않으면 스크립트를 닫는 태그로 인식돼 전체 스크립트가 제대로 실행되지 않을 수 있다.

올바른 이스케이핑의 예

자바스크립트 문자열 안에서

var string = '<!--\'"&lt;\\n+#]]>흫흣<\/script>';
var string = "<!--'\"&lt;\\n+#]]>흫흣<\/script>";

이런식의 선언 이후에 HTML 주석이 사용될 가능성이 있다고 한다면 아래처럼 <!-- 문자를 끊어서 출력해야 할 것이다.

var string = '<!' + '--\'"&lt;\\n+#]]>흫흣<\/script>';
var string = "<!" + "--'\"&lt;\\n+#]]>흫흣<\/script>";

HTML 본문과 속성에서

<p>&lt;!--'&quot;&amp;lt;\n+#]]&gt;흫흣&lt;/script&gt;</p>
<p title="&lt;!--'&quot;&amp;lt;\n+#]]&gt;흫흣&lt;/script&gt;"></p>

URL 쿼리 파라미터 안에서

http://example.com/?param=%3C%21--%27%22%26lt%3B%5Cn%2B%23%5D%5D%3E%ED%9D%AB%ED%9D%A3%3C%2Fscript%3E