Client Side Template Injection은 이용자의 입력 값이 client side template 프레임워크에 의해 템플릿으로 해석되어 렌더링될 때 발생된다.

 

 

 

Vue

 

Vue 혹은 Vue.js라고 불리는 프론트엔드 프레임워크는 2014년 출시된 프레임워크이며, 이용자 인터페이스나 Single Page Application을 빌드할 때 사용된다.

 

<script src="https://unpkg.com/vue@3"></script>

<div id="app">{{ message }}</div>

<script>
  Vue.createApp({
    data() {
      return {
        message: 'Hello Vue!'
      }
    }
  }).mount('#app')
</script>

 

위 코드에서 {{  }} 로 감싸진 부분이 Vue 템플릿 부분이다. 해당 템플릿 내에서 문자열을 표시하거나, 자바스크립트 표현식을 실행할 수 있다. 

만약 여기 템플릿 내부에 공격자의 입력이 삽입돼, Template Injection 취약점이 발생한다면, XSS 공격으로 이어질 수 있다.

 

 

<script src="https://unpkg.com/vue@3"></script>

<div id="app">
	<?php echo htmlspecialchars($_GET['msg']); ?>
</div>

<script>
  Vue.createApp({
    data() {
      return {
        message: 'Hello Vue!'
      }
    }
  }).mount('#app')
</script>

 

위 코드는 htmlspecialchars() 함수를 사용하여 $_GET['msg']를 출력하므로, 기본적으로 XSS 공격에는 안전하다.

 

하지만 Template Injection 의 경우에는 상황이 조금 다르다.

Vue.js를 포함한 JavaScript 기반 프레임워크에서는 템플릿을 파싱하고, 실행 시점에 동적으로 내용을 갱신하는 기능이 있기 때문에, 사용자가 제공한 입력이 Vue 템플릿으로 해석되어 Template Injection 이 발생할 수 있다.

 


 

Template Injection이 발생하는지 확인하는 간단한 방법은 템플릿을 이용해 산술 연산을 수행해보는 것이다.

{{1+1}} 과 같은 산술 연산 형태의 템플릿을 입력했을 때, 연산이 실행된 형태인 2가 출력된다면 Template Injection이 발생하는 것으로 확인할 수 있다.

 

 

Template Injection이 발생할 때, 이를 이용해 임의의 자바스크립트 코드를 실행하는 방법으로 생성자(constructor)를 사용하는 방법이 있다. Vue 템플릿 컨텍스트 내에서 생성자에 접근하여 악성 코드를 생성하고 이를 호출하여 XSS 공격을 수행할 수 있다.

Vue 템플릿 컨텍스트에서 생성자에 접근하는 대표적인 방법을 {{_Vue.h.constructor}} 를 사용하는 것이다.

이를 이용한 익스플로잇 코드는 다음과 같다.

{{_Vue.h.constructor("alert(1)")()}}     →  alert(1) 실행

 

 

 

 

 

AngularJS

AngularJS는 타입스크립트 기반의 오픈소스 프레임워크이며 CLI 도구에서 다양한 기능을 제공하기 때문에 개발을 편리하게 해주는 프레임워크 중 하나다.

 

<!doctype html>
<html ng-app>
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.3/angular.min.js"></script>
  </head>
  <body>
    <div>
      <label>Name:</label>
      <input type="text" ng-model="yourName" placeholder="Enter a name here">
      <hr>
      <h1>Hello {{yourName}}!</h1>
    </div>
  </body>
</html>

 

Vue와 마찬가지로 {{  }}로 감싸진 부분이 AngularJS 템플릿 부분이며, 해당 템플릿 내에서 문자열을 표시하거나, 자바스크립트 표현식을 실행할 수 있다. 만약 여기서 템플릿 내부에 공격자의 입력이 들어가 Template Injection 취약점이 발생한다면, XSS 공격으로 이어질 수 있다.

 

 

<!doctype html>
<html ng-app>
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.3/angular.min.js"></script>
  </head>
  <body>
      <?php echo htmlspecialchars($_GET['msg']); ?>
  </body>
</html>

 

htmlspecialchars 함수를 통해 msg 파라미터 출력하기 때문에 XSS 공격은 방지할 수 있지만, 출력되는 값이 AngularJS의 템플릿으로 사용될 수 있기 때문에 Template Injection이 발생한다.

 


 

{{1+1}} 과 같은 산술 연산 형태의 템플릿을 입력했을 때, 연산이 실행된 형태인 2가 출력된다면 Template Injection이 발생하는 것으로 확인할 수 있다.

 

Template Injection이 발생할 때, 이를 이용해 임의의 자바스크립트 코드를 실행하는 방법으로 생성자(constructor)를 사용하는 방법이 있다.  AngularJS 템플릿에서 생성자에 접근하기 위해서는 {{ constructor.constructor }}로 접근할 수 있다.

이를 이용하여 임의 코드에 해당하는 함수를 생성하고, 호출하여 공격할 수 있다

{{ constructor.constructor("alert(1)")() }}      →  alert(1) 실행

 

 

 

 

 

 

Content Security Policy (CSP)

  • 웹 보안을 강화하기 위해 웹 브라우저가 웹사이트의 리소스를 로드하는 방식을 제어하는 웹 보안 표준
  • 웹 사이트가 신뢰할 수 있는 리소스 출처를 명시적으로 지정함으로써, XSS(Cross-Site Scripting), 데이터 삽입 공격 등과 같은 일반적인 웹 공격을 방지
  • XSS 공격으로부터 피해를 최소화할 수 있는 방안이지만, XSS 공격의 피해를 완전히 무력화하기 위한 수단은 아님

 

사용 방법

CSP는 HTTP 헤더HTML의 <meta> 태그를 통해 정의됨

CSP 구문은 여러 정책 지시문(policy-directive)으로 구성되며, 각 지시문은 세미콜론(;)으로 구분됨

 

 

CSP 적용 예시

페이지 자원이 동일 오리진('self') 또는 특정 도메인(https://example.dreamhack.io)에서만 로드되도록 설정하는 예시

  • HTTP 헤더 사용
Content-Security-Policy: default-src 'self' https://example.dreamhack.io

 

  • HTML <meta> 태그 사용
<meta http-equiv="Content-Security-Policy" content="default-src 'self' https://example.dreamhack.io">

 

 

 

 

CSP는 웹 페이지 보안을 위해 인라인 코드와 문자열을 실행하는 메커니즘을 기본적으로 차단한다.

 

CSP 기본 정책 - 인라인 코드 (Inline Code)

  • 인라인 코드 차단: <script>alert(1);</script>와 같이 <script> 태그 안에 직접 코드를 삽입하는 인라인 코드를 유해하다고 간주하여 사용을 차단
  • 외부 스크립트 권장: 인라인 코드 대신 <script src="alert.js"></script> 처럼 scr 속성을 사용해 외부 스크립트를 로드하도록 권장
  • 허용되지 않는 인라인 코드 유형:
    • on 이벤트 핸들러 속성 (예: onclick="...")
    • javascript: URL 스킴
    • 인라인 <style> 태그와 style 속성도 차단되며, 외부 스타일시트 사용을 권장

CSP 기본 정책 - eval

  • eval 차단: eval() 함수과 같이 문자열을 실행 가능항 자바스크립트 고드로 변환하는 모든 메커니즘을 차단
  • 차단되는 예제: setTimeout("alert(1)", ....) 와 같이 문자열을 사용한 함수 호출은 차단
  • 허용되는 예제: setTimeout(function(){alert(1)}, ...) 처럼 인라인 함수 형태로 전달되는 경우는 허용

 

Policy Directive

  • <directive> <value> 형식으로 구성됨
    • <directive> (지시문): 어떤 종류의 리소스에 대해 출처를 제어할지 결정하는 역할
    • <value> (값): 지시문에서 제어할 리소스의 출처를 정의하며, 여러 출처를 공백으로 구분하여 나열 가능
지시문 설명
default-src -src로 끝나는 모든 지시문의 기본 동작을 제어합니다. 만약 CSP 구문 내에서 지정하지 않은 지시문이 존재한다면 default-src의 정의를 따라갑니다.
img-src 이미지를 로드할 수 있는 출처를 제어합니다.
script-src 스크립트 태그 관련 권한과 출처를 제어합니다.
style-src 스타일시트 관련 권한과 출처를 제어합니다.
child-src 페이지 내에 삽입된 프레임 컨텐츠에 대한 출처를 제어합니다.
base-uri 페이지의 <base> 태그에 나타날 수 있는 URL을 제어합니다.

 

값 (출처) 설명
*://example.com 출처의 scheme은 와일드카드를 이용해 표현할 수 있습니다.
https://*.example.com 출처의 호스트 서브도메인은 와일드카드를 이용해 표현할 수 있습니다. (단, 와일드카드는 호스트의 중간에 들어갈 수 없습니다. i.e) https://www.*.com, https://*.example.*
또한 서브도메인을 와일드카드로 표현할 시, 서브도메인이 붙어있지 않는 도메인은 포함되지 않습니다. i.e) https://*.example.com으로 출처를 표기할 경우, https://example.com은 포함 안됨
https://example.com:* 출처의 포트는 와일드카드를 이용해 표현할 수 있습니다.
none 모든 출처를 허용하지 않습니다.
self 페이지의 현재 출처 (Same Origin) 내에서 로드하는 리소스만 허용합니다.
unsafe-inline 예외적으로 인라인 코드의 사용을 허용합니다.
unsafe-eval 예외적으로 eval 과 같은 텍스트-자바스크립트 변환 메커니즘의 사용을 허용합니다
nonce-<base64-value> nonce 속성을 설정하여 예외적으로 인라인 코드를 사용합니다. <base64-value>는 반드시 요청마다 다른 난수 값으로 설정해야 합니다. 해당 출처를 설정하면 unsafe-inline 은 무시됩니다.
<hash-algorithm>-<base64-value> script 혹은 style 태그 내 코드의 해시를 표현합니다. 해당 출처를 설정하면 unsafe-inline은 무시됩니다.

 

 

 

 

CSP Examples

Content-Security-Policy: default-src 'self'

모든 리소스의 출처를 현재 페이지와 같은 출처로 제한함

 

Content-Security-Policy: default-src 'self' https://googleapis.com https://*.googleapis.com

모든 리소스의 출처를 현재 페이지와 같은 출처와 " https://googleapis.com", "https://*.googleapis.com"으로 제한함 

 

Content-Security-Policy: default-src 'self'; img-src *; script-src static.dreamhack.io

모든 리소스의 출처를 현재 페이지와 같은 출처로 제한하고, 이미지의 출처는 모든 호스트를 허용, 스크립트 태그의 출처는 "static.dreamhack.io"로 제한

 

Content-Security-Policy: child-src 'self' frame.dreamhack.io

페이지 내에 삽입된 프레임 콘텐츠 URL은 현재 페이지와 같은 출처와 "frame.dreamhack.io"내의 컨텐츠만 로드할 수 있음

 

Content-Security-Policy: base-uri 'none'

base 태그의 URL은 어느 것도 허용하지 않음

 

Content-Security-Policy: script-src 'unsafe-eval'

자바스트립트 코드 내에 eval 과 같은 텍스트-자바스크립트 변환 메커니즘의 사용을 허용

 

Content-Security-Policy: script-src 'sha256-5jFwrAK0UV47oFbVg/iCCBbxD8X1w+QvoOUepu4C2YA='

스트립트 태그 내의 코드 혹은 src 속성으로 지정된 파일의 sha256 해시를 base64로 인코딩한 결과가 "5jFwrAK0UV47oFbVg/iCCBbxD8X1w+QvoOUepu4C2YA="와 다르다면 스크립트 로드에 실패

 

 

 

 

 

+ Recent posts