가이드라인

주입 - XSS

사이트 간 스크립팅(XSS)은 공격자가 제어하는 스크립트가 다른 사용자의 브라우저에서 실행되도록 유도하는 또 다른 유형의 인젝션 취약점입니다. XSS는 HTML/JavaScript 인젝션 취약점으로 간주되기도 합니다.

XSS 취약점의 영향은 해당 취약점이 존재하는 맥락에 따라 크게 달라집니다. 스크립트가 실행되는 페이지에서 정보를 추출하거나 페이지 상태를 편집하여 피해자를 대신해 특정 작업을 수행하는 것까지 그 범위가 다양합니다.

발생할 수 있는 XSS 유형을 살펴보겠습니다.

XSS 유형

XSS는 구분을 돕기 위해 몇 가지 범주로 나눌 수 있습니다. 페이로드가 전달되는 방식과 진입점이 어디인지에 따라 분류됩니다.

페이로드 전달 벡터 (저장형 vs 반사형)

공격자가 XSS 페이로드를 전달하는 방법에는 두 가지가 있습니다.

  • 저장형 XSS: 데이터베이스와 같이 사용자 제어 데이터가 저장되는 곳을 통해 다른 사용자에게 렌더링되는 방식
  • 반사형 XSS: 사용자가 클릭한 URL/쿼리 문자열을 통해 전달되는 사용자 제공 페이로드를 통한 방식

가장 중요한 차이점은 반사형 XSS는 일반적으로 사용자의 상호작용에 의존하며 악성 페이로드가 포함된 링크를 여는 사용자에게만 영향을 미친다는 점입니다. 반면, 저장형 XSS는 페이로드를 렌더링하는 페이지를 열기만 해도 한 명 이상의 사용자에게 실행될 수 있습니다.

페이로드 위치 (DOM 기반 vs 비 DOM 기반)

페이로드가 주입되는 위치에 따라 해당 취약점을 "DOM" 취약점으로 분류할지 여부가 결정됩니다. 이는 페이로드가 렌더링되는 위치에 따른 구분입니다.

  • 비 DOM 기반 XSS: 태그 내부든 속성 내부든 상관없이 HTML 내부에 페이로드가 렌더링되는 경우
  • DOM 기반 XSS: `<script>` 태그나 `onclick=""`과 같은 이벤트 핸들러 등 JavaScript 내부에 페이로드가 렌더링되는 경우

XSS - 방어 기본 원칙

이 섹션에서는 XSS 방어의 기초가 되는 원칙들을 살펴보겠습니다. 이러한 기본 사항을 이해하는 것은 필수적이지만, 실제 환경에서는 XSS 방어의 99%를 템플릿 라이브러리에 의존해야 합니다.

브라우저에서 실행되는 마크업이나 코드로 렌더링되는 템플릿을 작성할 때, XSS를 방어하는 핵심은 *인코딩*입니다. 여기서 인코딩이란 일련의 문자를 가져와 인터프리터가 특정 방식으로 처리하도록 형식을 변경하는 것을 의미합니다.

하지만 어떤 유형의 인코딩을 사용할지는 데이터가 사용될 위치나 맥락에 따라 달라집니다.

  • 태그 내부 (예: `<div>사용자 입력값</div>`): **HTML 인코딩**
  • 속성 내부 (예: `<input placeholder="사용자 입력값"></input>`): **속성 인코딩**
  • JavaScript 내부 (예: `<script>x = "사용자 입력값";</script>`): **JavaScript 인코딩**

일부 프레임워크는 인코딩을 주된 방어 수단으로 사용하지 않고, 위험할 수 있는 콘텐츠로부터 값을 제거하는 샌티타이제이션(Sanitization)을 활용합니다. 이는 고려해야 할 예외 상황이 많은 훨씬 복잡한 과정입니다. 직접 샌티타이제이션 루틴을 구현하는 것은 권장되지 않습니다.

예시

다양한 언어의 예시를 통해 실제로 어떻게 작동하는지 살펴보겠습니다.

C# - 보안 취약: Razor

`IHtmlContent` 객체 앞에 `@`를 붙이면, 해당 값은 인코딩 없이 템플릿에 직접 삽입됩니다.

<!--- 안전하지 않음: htmlSnippet이 이스케이프 처리 없이 해석됨 --->
@Html.Raw(htmlSnippet)

C# - 보안: Razor

기본적으로 Razor 템플릿에서 `@`가 붙은 모든 `string`은 HTML 이스케이프 처리됩니다.

<!--- 안전함: htmlSnippet이 HTML 이스케이프 처리됨 --->
@htmlSnippet

Java - 보안: JSP

`c:out`을 사용하면 기본적으로 XML 이스케이프 처리가 적용됩니다(`escapeXml` 속성으로 변경 가능). 이는 HTML 컨텍스트에서의 XSS는 방어하지만, 다른 컨텍스트에서는 그렇지 않습니다.

<div><c:out value="<%= author %>" /></div>

Java - 보안: (fnxml)

Similar to above, you can also directly call `fn:escapeXml`, which will XML escape the input given to it. This will also only protect in a HTML context.

<div>${fn:escapeXml(author)}</div>

Javascript - 보안 취약: Angular innerHtml

`innerHTML` 속성을 지정하면 이름에서 알 수 있듯이 출력 인코딩이 비활성화되어 XSS 위험에 노출됩니다.

<!--- 안전하지 않음: htmlSnippet이 인코딩 없이 해석됨 --->
<p [innerHTML]="htmlSnippet"></p>

Javascript - 보안: Angular 보간법(Interpolation)

이중 중괄호(`{{` 및 `}}`)를 사용하는 Angular의 텍스트 보간법은 출력을 HTML 이스케이프 처리하여 XSS로부터 보호합니다.

<!--- 안전함: htmlSnippet이 인코딩된 후 해석됨 --->
<p>{{htmlSnippet}}</p>

Javascript - 보안 취약: React dangerousInnerHtml

`dangerouslySetInnerHTML` 속성을 지정하면 이름에서 알 수 있듯이 출력 인코딩이 비활성화되어 XSS 위험에 노출됩니다.

<!--- 위험: 이름에서 알 수 있듯이 dangerouslySetInnerHTML 속성은 출력을 이스케이프하지 않으므로 위험합니다 ---&gt;
<div dangerouslySetInnerHTML={{ __html: htmlSnippet }} /&gt;;

Javascript - 안전하지 않음: React 보간

중괄호(`{` 및 `}`)를 사용하는 React의 텍스트 보간은 출력을 HTML 이스케이프 처리하여 XSS로부터 보호합니다.

<!--- 안전: htmlSnippet은 인코딩된 후 해석됩니다 ---&gt;
<div&gt;{htmlSnippet}</div&gt;

Python - 안전하지 않음: Django

Django 템플릿에서 `safe` 필터를 사용하면 자동 출력 이스케이프가 비활성화되어 XSS로부터 보호되지 않습니다.

<!--- 위험: htmlSnippet은 HTML 인코딩되지 않습니다 ---&gt;
<div&gt;{{ htmlSnippet | safe }}</div&gt;

Python - 안전: Django

이중 중괄호(`{{` 및 `}}`)를 사용하는 Django의 텍스트 보간은 출력을 HTML 이스케이프 처리하여 XSS로부터 보호합니다.

<!--- 안전: htmlSnippet은 HTML 인코딩됩니다 ---&gt;
<div&gt;{{ name }}</div&gt;

‍

태그라인

실무에 바로 적용하는 보안 코딩

OWASP Top 10과 같은 소프트웨어 취약점을 이해하고 완화하기 위한 보안 코딩 가이드라인을 살펴보고, 실제 앱 시뮬레이션을 통해 실습할 수 있는 가이드형 교육 미션을 시작해 보세요.

추가 가이드라인
데모 예약하기