회원가입 | 고객센터 |
DESIGNONEX
디자인원엑스
Q&A
지식공유
공지사항
DX마켓
통계
로그인 회원가입
고객센터

DX Security — XSS 보안 플러그인

D DX관리자
2026.09.03 01:08 4 0



DX Security — XSS 방어 v1.0.0 안내서

DesignOneX CMS XSS 기업 납품 수준 2차 방어 플러그인
PHP 5.6+ · IIS / Apache / Nginx · 공유호스팅 완전 호환
코어(DxSanitizer.php / Secure.php) 수정 없음 — 훅으로만 동작
OWASP XSS Prevention Cheat Sheet 준수



목차

  1. XSS란 무엇인가
  2. DXCMS 코어의 현재 XSS 방어 수준
  3. 코어가 막지 못하는 것 — 이 플러그인의 역할
  4. 설치 방법
  5. 방어층 상세 설명
    • XS1. CSP Nonce 기반 강화
    • XS2. DxSanitizer 우회 패턴 보완
    • XS3. JSON 출력 XSS 방어
    • XS4. URL 출력 이스케이프 강화
    • XS5. 관리자 HTML 콘텐츠 추가 검증
    • XS6. WAF XSS 패턴 강화
    • XS7. 출력 컨텍스트별 이스케이프 표준 API
    • XS8. 보안 감사 로그
  6. 훅 실행 타이밍
  7. 보안 로그 읽는 법
  8. 보안 강점 요약
  9. 방어하지 못하는 영역
  10. 테마 개발자 — CSP Nonce 적용
  11. 개발자 API 가이드
  12. 자주 묻는 질문


1. XSS란 무엇인가

XSS(Cross-Site Scripting)는 공격자가 웹 페이지에 악성 JavaScript를 삽입하여 다른 사용자의 브라우저에서 실행시키는 공격입니다.


공격 유형

① Stored XSS (저장형):
   공격자가 게시판에 <script>...</script> 삽입
   → 서버에 저장
   → 다른 사용자가 페이지 조회 시 스크립트 실행
   → 세션 탈취, 피싱, 악성코드 배포

② Reflected XSS (반사형):
   ?q=<script>...</script> URL 공유
   → 피해자가 링크 클릭
   → 서버가 검색어를 그대로 응답에 포함
   → 즉시 실행

③ DOM XSS:
   JavaScript에서 document.write(location.hash) 등
   서버를 거치지 않고 DOM을 직접 조작
   → 서버 측 필터링을 완전히 우회


XSS로 할 수 있는 것

document.cookie          → 세션 쿠키 탈취 (HttpOnly 없으면)
fetch('/api/...', {credentials: 'include'})  → CSRF 공격
localStorage, sessionStorage  → 저장 데이터 탈취
document.forms[0]        → 입력 폼 데이터 도용
window.location          → 피싱 페이지로 리다이렉트
키로거 구현              → 모든 키 입력 수집


XSS와 CSP의 관계

CSP 없음: 삽입된 스크립트 즉시 실행
CSP with 'unsafe-inline': 삽입된 인라인 스크립트 여전히 실행
CSP with nonce: 서버 발급 nonce가 없는 스크립트는 실행 불가
                → 공격자가 nonce를 예측할 수 없으므로 효과적 차단


2. DXCMS 코어의 현재 XSS 방어 수준

코어 DxSanitizer.phpSecure.php를 직접 분석한 결과입니다.


DxSanitizer::editorContent() — 매우 우수

저장 전 HTML 정화 파이프라인:
  1. 차단 태그 완전 제거 (script, iframe, object, embed, form, svg 등)
  2. 이벤트 핸들러 제거 (onclick, onerror, onload 등 모든 on* 속성)
  3. blocked:/blocked: 프로토콜 차단
  4. data: URI img src 차단
  5. PHP 태그 제거
  6. CSS  제거
  7. Allowlist 태그만 유지 (strip_tags)
  8. 각 태그의 속성 Allowlist 검사
  9. href/src blocked: 차단
  10. target="_blank" → rel="noopener noreferrer" 자동 추가
  11. style 속성 CSS 속성 Allowlist + 위험 값 제거


보안 헤더 — 양호

X-Frame-Options: SAMEORIGIN         Clickjacking 방어
X-Content-Type-Options: nosniff     MIME Sniffing 방어
X-XSS-Protection: 1; mode=block    구버전 브라우저 XSS 필터
Referrer-Policy                     정보 유출 최소화
Permissions-Policy                  카메라/마이크/위치 차단
HSTS (HTTPS 환경)                   프로토콜 다운그레이드 방어
CSP 헤더 (기본 구조)                 콘텐츠 출처 제한

XSS 방어 자체 평가: 8.5/10 — 우수



3. 코어가 막지 못하는 것 — 이 플러그인의 역할

❌ 미비점 1: CSP의 'unsafe-inline' — 가장 중요

코어 CSP:

script-src 'self' 'unsafe-inline' https: blob:

'unsafe-inline'이 있으면 공격자가 삽입한 인라인 스크립트도 실행됩니다.

DxSanitizer가 필터링에 실패하거나
우회 기법으로 <script> 태그가 삽입되면
'unsafe-inline'으로 인해 그대로 실행됩니다.

nonce 방식:
  서버가 매 요청마다 랜덤 nonce 발급
  <script nonce="a3f29c...">  ← 이 스크립트만 실행
  삽입된 <script>             ← nonce 없음 → 실행 차단

코어에 getCspNonce() 메서드가 있지만 발급된 nonce가 CSP 헤더에 포함되지 않습니다.


❌ 미비점 2: DxSanitizer 우회 패턴

srcset 속성:
  <img srcset="blocked:alert(1)"> → 일부 브라우저 실행

HTML Entity 우회:
  &#106;avascript: → html_entity_decode 후 blocked:

CSS -moz-binding (구버전 Firefox):
  style="-moz-binding:url(evil.xml#xss)"

공백 우회:
  j a v a s c r i p t : → 공백 제거 후 blocked:

Mutation XSS:
  파싱 중 DOM이 변형되어 발생하는 XSS


❌ 미비점 3: JSON 출력 XSS

// 코어의 일반 json_encode()
echo json_encode($data);
// {"name":"<script>alert(1)</script>"}
// HTML 컨텍스트에서 <script>가 그대로 출력됨


4. 설치 방법

(DXCMS 루트)/
└── plugins/
    └── dx-security-xss/
        ├── manifest.php
        └── plugin.php
관리자 → 플러그인 → dx-security-xss → 활성화


5. 방어층 상세 설명


XS1. CSP Nonce 기반 강화

nonce CSP의 동작 원리

1. 서버가 요청마다 랜덤 nonce 생성
   nonce = base64(random_bytes(16))
   예: "a3f29c11d7e8b4c2..."

2. CSP 헤더에 nonce 포함
   script-src 'nonce-a3f29c11d7e8b4c2...'

3. 정상 스크립트 태그
   <script nonce="a3f29c11d7e8b4c2...">
     // 실행됨 ✅
   </script>

4. 공격자가 삽입한 스크립트
   <script>alert(document.cookie)</script>
   // nonce 없음 → 실행 차단 ✅


호환 모드 vs 엄격 모드

모드 CSP 설정 효과 요구사항
호환 (기본) nonce + unsafe-inline nonce 지원 시작, 기존 스크립트 유지 없음
엄격 nonce만 인라인 스크립트 완전 차단 테마의 모든 <script>에 nonce 추가


엄격 모드 활성화 (plugin.php에서):

define('DX_XSS_STRICT_CSP', true);


추가된 CSP 지시어

코어에 없는 지시어를 추가합니다:

base-uri 'self'      <base href="..."> XSS 방어
form-action 'self'   폼 action 외부 제출 차단
frame-ancestors 'self'  Clickjacking 이중 방어


XS2. DxSanitizer 우회 패턴 보완

코어 DxSanitizer::clean() 처리 후 추가로 적용됩니다.


보완 항목 6가지

① srcset 속성 제거

<img srcset="blocked:alert(1)"> → 제거


② HTML Entity 우회 탐지

&#106;avascript: → decode → blocked: → 탐지 + 엔티티 제거


③ CSS 우회 패턴

-moz-binding: url(evil.xml#xss);  → blocked:
behavior: url(evil.htc);           → blocked:


④ XML Namespace 속성

<a xml:base="blocked:">        → data-blocked=
<use xlink:href="evil.svg#xss">   → data-blocked=


⑤ 신규 이벤트 핸들러 우회

\x00onclick= (Null byte 우회) → data-blocked=


⑥ 공백 우회 정규화

j a v a s c r i p t : → 공백 제거 → 탐지 → blocked:


XS3. JSON 출력 XSS 방어

dx_json_encode_safe() 함수

// 기본 json_encode() — 위험
echo json_encode(['html' => '<script>alert(1)</script>']);
// {"html":"<script>alert(1)</script>"}  → XSS 가능

// dx_json_encode_safe() — 안전
echo dx_json_encode_safe(['html' => '<script>alert(1)</script>']);
// {"html":"\u003Cscript\u003Ealert(1)\u003C\/script\u003E"}
// → 브라우저가 HTML로 파싱하지 않음
플래그 변환
JSON_HEX_TAG <\u003C, >\u003E
JSON_HEX_AMP &\u0026
JSON_HEX_APOS '\u0027
JSON_HEX_QUOT "\u0022


XS4. URL 출력 이스케이프 강화

dx_esc_url() — 코어 Secure::safeUrl() 대비 강화

추가 탐지:
  ① Unicode 공백/제어문자 제거
  ② HTML Entity 디코딩 후 재검사
  ③ 로그 기록

탐지 우회 방지:
  j\u0000avascript: → 제어문자 제거 → blocked: 탐지
  &#106;avascript: → decode → blocked: 탐지


XS5. 관리자 HTML 콘텐츠 추가 검증

관리자가 HTML을 직접 입력하는 영역(메뉴, 배너, 위젯)에서 추가 검증합니다.

탐지 및 제거:
  ① <script> 태그
  ② 이벤트 핸들러 (onclick, onerror 등)
  ③ blocked:/blocked: 프로토콜

관리자라도 스크립트 삽입은 허용하지 않습니다.


XS6. WAF XSS 패턴 강화

코어 WAF 3가지 패턴에 12가지 추가 패턴을 보강합니다.

추가 패턴 탐지 대상
document.cookie 쿠키 탈취 시도
window.location 리다이렉션 시도
.innerHTML = DOM XSS
eval() 동적 코드 실행
<svg> SVG 기반 XSS
onfocus, onblur HTML5 이벤트
CSS XSS
data:text/html Data URI XSS
template literal + script 템플릿 리터럴 XSS

WAF는 탐지·로그 목적입니다. 에디터 본문 필드는 DxSanitizer가 처리하므로 제외합니다.



XS7. 출력 컨텍스트별 이스케이프 표준 API

XSS 방어의 핵심은 출력 컨텍스트에 맞는 이스케이프입니다.

컨텍스트 함수 사용 위치
HTML dx_esc_html($str) <div><?= dx_esc_html($input) ?></div>
HTML 속성 dx_esc_attr($str) <input value="<?= dx_esc_attr($input) ?>">
JavaScript dx_esc_js($str) var x = "<?= dx_esc_js($input) ?>"
CSS dx_esc_css($str) style="color: <?= dx_esc_css($color) ?>"
URL dx_esc_url($url) <a href="<?= dx_esc_url($url) ?>">
JSON dx_json_encode_safe($data) <script>var d = <?= dx_json_encode_safe($data) ?></script>


왜 컨텍스트별로 다른 함수가 필요한가

HTML 컨텍스트:  < → &lt;  는 완전한 방어
JS 컨텍스트:    < → &lt;  는 불완전 (</script> 우회 가능)
                < → \u003C 가 올바른 방어
URL 컨텍스트:   blocked: 를 별도로 차단해야 함
CSS 컨텍스트:   expression() 를 별도로 차단해야 함


6. 훅 실행 타이밍

[모든 요청]DxExtend::runTop()
  └── dx_extend_top (priority 20): [XS1] CSP Nonce 헤더 교체
  └── dx_extend_top (priority 16): [XS6] WAF XSS 패턴 강화

[게시물 저장]
  boards/handler.php
    └── DxSanitizer::editorContent() (코어)
    └── dx_after_sanitize_content (priority 1): [XS2] 추가 정화
    └── dx_before_post_save (priority 1): [XS2] content 추가 정화

[관리자 HTML 저장]
  └── dx_before_admin_html_save (priority 1): [XS5] 관리자 검증

[JSON API 응답]
  └── dx_before_json_response (priority 1): [XS3] XSS 패턴 감지


7. 보안 로그 읽는 법

이벤트 타입

타입 의미
XSS:EXTRA_SANITIZE_APPLIED DxSanitizer 이후 추가 정화 적용
XSS:CONTENT_EXTRA_CLEANED 게시물 저장 전 추가 정화
XSS:ENTITY_ENCODING_BYPASS HTML Entity 우회 탐지
XSS:WHITESPACE_BYPASS 공백 우회 blocked: 탐지
XSS:URL_INJECTION_BLOCKED 위험 URL 프로토콜 차단
XSS:JSON_XSS_PATTERN JSON 응답에서 XSS 패턴 감지
XSS:ADMIN_HTML_SCRIPT_TAG 관리자 영역 script 태그 감지
XSS:ADMIN_HTML_EVENT_HANDLER 관리자 영역 이벤트 핸들러 감지
XSS:ADMIN_HTML_DANGEROUS_PROTOCOL 관리자 영역 위험 프로토콜 감지
XSS:WAF_XSS_PATTERN WAF 추가 XSS 패턴 탐지


로그 예시

# HTML Entity 우회 탐지
[2026-09-01 09:00:01][XSS:ENTITY_ENCODING_BYPASS][IP:45.33.x.x][...][UID:5][URI:/board/free/write]
html_entity_decode attack detected

# 관리자 영역 script 태그
[2026-09-01 14:22:10][XSS:ADMIN_HTML_SCRIPT_TAG][IP:121.131.x.x][...][UID:1][URI:/admin/menu]
script tag in admin HTML area

# WAF DOM XSS 패턴
[2026-09-01 16:45:03][XSS:WAF_XSS_PATTERN][IP:58.29.x.x][...][UID:0][URI:/api/comment]
source=POST key=content pattern=/document\s*\.\s*(cookie|...


8. 보안 강점 요약

XSS 공격 테스트 결과

공격 결과 방어 레이어
<script>alert(1)</script> ✅ 방어 DxSanitizer (코어)
<img> ✅ 방어 DxSanitizer (코어)
<svg><script>alert(1)</script></svg> ✅ 방어 DxSanitizer + XS2
&#106;avascript:alert(1) ✅ 방어 XS2 Entity 우회 탐지
j a v a s c r i p t : ✅ 방어 XS2 공백 우회 탐지
<img srcset="blocked:..."> ✅ 방어 XS2 srcset 제거
JSON의 <script> ✅ 방어 XS3 json_encode_safe
blocked: URL ✅ 방어 XS4 URL Escape
DOM XSS ✅ 탐지 XS6 WAF 탐지
관리자 영역 script ✅ 방어 XS5
인라인 XSS (CSP 없는 경우) ✅ 방어 XS1 CSP nonce


9. 방어하지 못하는 영역

취약점 이유 추가 대책
DOM XSS (서버 미통과) PHP 서버 측 필터링 불가 DOMPurify 클라이언트 라이브러리
CSP 엄격 모드 적용 전 기존 스크립트 테마 수정 필요 테마의 모든 <script>에 nonce 추가
저장된 XSS가 이미 DB에 있는 경우 기존 데이터 소급 적용 불가 DB 전체 재정화 필요
HttpOnly 없는 쿠키 다른 플러그인/설정 코어 Secure.php 설정 확인


10. 테마 개발자 — CSP Nonce 적용

엄격 모드 활성화 후 테마 수정 방법

plugin.php에서:

define('DX_XSS_STRICT_CSP', true);

테마의 모든 인라인 <script> 태그에 nonce 추가:

<!-- 수정 전 -->
<script>
var config = <?= json_encode($config) ?>;
</script>

<!-- 수정 후 -->
<script nonce="<?= dx_csp_nonce() ?>">
var config = <?= dx_json_encode_safe($config) ?>;
</script>

인라인 <style> 태그도 동일:

<style nonce="<?= dx_csp_nonce() ?>">
.example { color: red; }
</style>


11. 개발자 API 가이드

출력 이스케이프

// HTML 출력
echo '<div>' . dx_esc_html($userInput) . '</div>';

// HTML 속성
echo '<input value="' . dx_esc_attr($userInput) . '">';

// JavaScript 변수에 PHP 값 주입
echo '<script nonce="' . dx_csp_nonce() . '">';
echo 'var name = "' . dx_esc_js($userName) . '";';
echo '</script>';

// URL
echo '<a href="' . dx_esc_url($url) . '">링크</a>';

// JSON 출력 (script 태그 안)
echo '<script nonce="' . dx_csp_nonce() . '">';
echo 'var data = ' . dx_json_encode_safe($data) . ';';
echo '</script>';


에디터 콘텐츠 수동 정화

// 기존 코어 방식
$clean = DxSanitizer::editorContent($rawHtml);

// 이 플러그인 추가 정화 포함
$clean = DxSanitizer::editorContent($rawHtml);
$args = dx_run_hook('dx_after_sanitize_content', array('content' => $clean));
$clean = isset($args['content']) ? $args['content'] : $clean;


12. 자주 묻는 질문

Q. CSP를 활성화하면 외부 스크립트(jQuery CDN)가 차단되나요?

아니요. CSP에 https: 가 포함되어 있어 외부 HTTPS 스크립트는 허용됩니다. <script src="https://code.jquery.com/jquery.min.js"> 는 정상 동작합니다.


Q. 엄격 모드를 사용하려면 테마를 전부 수정해야 하나요?

네. 테마의 인라인 <script> 태그 모두에 nonce="<?= dx_csp_nonce() ?>" 를 추가해야 합니다. 수정하지 않으면 해당 스크립트가 차단됩니다. 먼저 호환 모드(기본)로 운영하면서 테마를 수정한 후 엄격 모드로 전환하세요.


Q. DxSanitizer가 이미 잘 되어 있는데 추가 정화가 필요한가요?

DxSanitizer는 매우 잘 설계되어 있습니다. 이 플러그인은 알려진 우회 기법(HTML Entity, 공백 우회, srcset, Mutation XSS 등)을 추가로 막습니다. 보안은 레이어가 많을수록 좋습니다.


DX Security — XSS 방어 v1.0.0 — DesignOneX
https://designonex.com
 

라이선스

디자인원엑스 라이선스 참고
https://designonex.com/notice/view/1787577858493247

첨부파일 1개 회원전용

댓글0

로그인 후 댓글을 작성할 수 있습니다.
Plugin 49
45
전체 회원
1,375
전체 게시글
2,737
전체 댓글
38
오늘 방문
53,476
전체 방문
0
현재 접속
인기글 7일 이내
최신글
최신댓글
내 플레이리스트
플레이리스트가 비어있습니다
스튜디오 게시판에서
플레이리스트에 담기 버튼을
눌러보세요
목록
목록