
DX Security — Path Traversal 방어 v1.0.0 안내서
DesignOneX CMS Path Traversal 기업 납품 수준 방어 플러그인
PHP 5.6+ · IIS / Apache / Nginx · 공유호스팅 완전 호환
코어·테마·플러그인 정상 동작 완전 보장
코어(functions.php / download.php 등) 수정 없음 — 훅으로만 동작
목차
- Path Traversal이란 무엇인가
- DXCMS 코어의 현재 방어 수준
- 코어가 막지 못하는 것 — 이 플러그인의 역할
- 설치 방법
- 방어층 상세 설명
- PT1. 강화된 Canonical Path 검증 API
- PT2. PHP Wrapper 차단
- PT3. Double / Triple Encoding 우회 차단
- PT4. NULL Byte 차단
- PT5. Windows 경로 공격 차단
- PT6. WAF — 요청 Path Traversal 패턴 탐지
- PT7. 보안 감사 로그
- WAF 설계 원칙 — 왜 차단하지 않는가
- 보안 로그 읽는 법
- 보안 강점 요약 — 공격 테스트 결과
- 개발자 API 가이드
- 자주 묻는 질문
1. Path Traversal이란 무엇인가
사용자가 입력한 경로를 이용하여 서버가 허용된 디렉토리 밖의 파일에 접근하는 공격입니다.
공격 시나리오
취약한 코드:
$file = $_GET['file'];
readfile('/var/www/uploads/' . $file);
공격 URL:
?file=../../etc/passwd
실제 접근 경로:
/var/www/uploads/../../etc/passwd
→ 정규화 후: /etc/passwd
→ 서버 비밀번호 파일 노출
다양한 우회 기법
기본: ../../../etc/passwd
백슬래시: ..\..\..\etc\passwd
URL 인코딩: %2e%2e%2fetc%2fpasswd
Double 인코딩: %252e%252e%252fetc%252fpasswd
NULL Byte: ../../etc/passwd%00.jpg
PHP Wrapper: php://filter/read=string/resource=../../config.php
Phar: phar://uploads/evil.zip/evil.php
UNC Path: \\server\share\evil.php (Windows)
혼합: ..%2f..%5c..%2fetc/passwd
Path Traversal → RCE 연계
Path Traversal
↓
PHP 파일 접근 (LFI)
↓
include('/uploads/evil.jpg') ← evil.jpg 안에 PHP 코드
↓
원격 코드 실행 (RCE)
2. DXCMS 코어의 현재 방어 수준
코어 소스를 직접 분석한 결과입니다.
코어의 강점 — 이미 잘 구현된 것
dx_path_inside() 함수 (core/functions.php):
function dx_path_inside($path, $base)
{
$path = dx_realpath($path); // realpath() + 백슬래시 정규화
$base = dx_realpath($base);
if ($path === false || $base === false) return false;
$base = rtrim($base, '/');
// IIS/Windows: 대소문자 무시 비교
if (DIRECTORY_SEPARATOR === '\\') {
return stripos($path, $base . '/') === 0 || ...
}
return strpos($path, $base . '/') === 0 || $path === $base;
}
download.php:
// file_id(DB) 기반 → 사용자가 경로를 직접 입력 불가
// dx_path_inside() 검증 후 readfile()
if (!dx_path_inside($fullPath, DX_DATA.'/uploads') || !is_file($fullPath)) {
http_response_code(404); exit('파일이 존재하지 않습니다.');
}
DxTheme 스킨 로딩:
// 스킨명을 알파벳/숫자만 허용
$skin = preg_replace('/[^a-z0-9_\-]/', '', $skin);
PluginRegistry, Dispatcher:
- Registry 기반 로딩 — 외부 입력이 직접 include 경로로 사용되지 않음
- 업로드 파일명 — CSPRNG 랜덤 파일명으로 저장
코어 자체 평가: 8/10 — 구조적으로 매우 안전
3. 코어가 막지 못하는 것 — 이 플러그인의 역할
❌ 미비점 1: PHP Wrapper 입력 탐지 없음
dx_path_inside('php://filter/read=convert.base64-encode/resource=../../config.php', $base)
→ realpath('php://filter/...') = false
→ dx_path_inside 반환값 = false
→ 차단은 되지만 로그 기록 없음
→ 탐지 안 됨 — 공격 시도 인식 불가
❌ 미비점 2: Double Encoding 우회 탐지 없음
?file=%252e%252e%252fconfig.php
→ 서버 URL decode: %2e%2e%2fconfig.php
→ 코어 처리: realpath(%2e%2e%2fconfig.php) = false (차단됨)
→ 그러나 탐지 로그 없음
→ 공격 패턴 모니터링 불가
❌ 미비점 3: NULL Byte 탐지 없음
?file=../../config.php%00.jpg
→ 코어: realpath 처리 중 %00 포함으로 false 반환 (차단됨)
→ 탐지 로그 없음
❌ 미비점 4: 새 파일 생성 경로 검증 불가
realpath()는 존재하는 파일만 처리 가능
새 파일 경로 (/uploads/new/file.jpg) → realpath() = false → 검증 실패
→ 플러그인 개발자가 새 파일 경로를 안전하게 검증할 표준 함수 없음
4. 설치 방법
(DXCMS 루트)/
└── plugins/
└── dx-security-path/
├── manifest.php
└── plugin.php
관리자 → 플러그인 → dx-security-path → 활성화
5. 방어층 상세 설명
PT1. 강화된 Canonical Path 검증 API
코어 dx_path_inside()의 기능을 확장한 dx_pt_safe_path() 함수를 제공합니다.
코어 dx_path_inside()와의 차이
| 기능 | 코어 | 이 플러그인 |
|---|---|---|
| realpath() 검증 | ✅ | ✅ |
| Root boundary 확인 | ✅ | ✅ |
| Windows 대소문자 처리 | ✅ | ✅ |
| PHP Wrapper 사전 차단 | ❌ | ✅ |
| Double Encoding 탐지 | ❌ | ✅ |
| NULL Byte 차단 | ❌ | ✅ |
| Windows 절대경로 차단 | ❌ | ✅ |
| UNC Path 차단 | ❌ | ✅ |
| 파일 미존재 시 부모 디렉토리 검증 | ❌ | ✅ |
| 보안 로그 자동 기록 | ❌ | ✅ |
PT2. PHP Wrapper 차단
LFI와 결합 시 RCE로 발전하는 가장 위험한 우회 기법입니다.
차단 Wrapper:
php:// php://filter/read=.../resource=../../config.php
phar:// phar://uploads/evil.zip/webshell.php
zip:// zip://uploads/evil.zip#webshell.php
rar:// rar://uploads/evil.rar#webshell.php
data:// data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7Pz4=
file:// file:///etc/passwd
glob:// glob://uploads/*.php
expect:// expect://id (RCE 직접 가능)
탐지 방식: 입력값에서 대소문자 무관하게 패턴 검사 (strtolower() 후 비교).
PT3. Double / Triple Encoding 우회 차단
1차 Encoding: ../ → %2e%2e%2f
2차 Encoding: %2e%2e%2f → %252e%252e%252f
3차 Encoding: ... → %25252e...
탐지 방식:
1. rawurldecode() 1회 → 결과에서 .. 또는 Wrapper 검사
2. rawurldecode() 2회 → 결과에서 .. 또는 Wrapper 검사
→ 탐지 시 차단 + 로그
PT4. NULL Byte 차단
공격: config.php%00.jpg
효과: C 라이브러리가 \0에서 문자열 절단
→ config.php만 인식 → PHP 파일 처리
탐지:
\x00 (바이너리 NULL)
%00 (URL 인코딩된 NULL)
PT5. Windows 경로 공격 차단
IIS/Windows 환경의 DXCMS에서 특히 중요합니다.
차단 패턴:
C:\Windows\System32\ Windows 드라이브 절대경로
C:/Windows/ 슬래시 혼합
\\server\share UNC Path
//server/share 슬래시 UNC
Windows Device 이름 차단:
CON PRN AUX NUL
COM1~COM9 LPT1~LPT9
(이 이름들은 파일명으로 사용 시 OS 오동작 유발)
PT6. WAF — 요청 Path Traversal 패턴 탐지
모든 요청의 파일 관련 파라미터를 검사합니다.
검사 파라미터 목록
file, path, dir, folder, document, page, template,
theme, skin, layout, view, include, module, plugin,
load, read, src, source, filename, filepath, name,
target, location
탐지 패턴 (17가지)
../ ..\ 기본 Path Traversal
..%2f ..%5c URL 인코딩
%2e%2e/ 인코딩된 점
%252e Double Encoding
\x00 NULL Byte
php:// phar:// PHP Wrapper
data:// expect://
./.*\.(php|phtml|phar) 경로탈출+PHP
/etc/passwd /etc/shadow Linux 파일
/proc/self Linux proc
C:\ C:/ \\ Windows 경로
6. WAF 설계 원칙 — 왜 차단하지 않는가
이 플러그인의 WAF는 탐지·로그만 수행합니다. 요청을 차단하지 않습니다.
이유
게시물 내용에 "../" 문자열이 있을 수 있습니다.
예: "설정파일은 ../config/ 폴더에 있습니다."
→ WAF가 차단하면 정상 게시물 작성 불가
코드 예제 포함 게시물:
예: "include('../lib/helper.php');"
→ 차단하면 개발 커뮤니티 사이트 운영 불가
URL 파라미터에 경로가 있을 수 있습니다:
?redirect=/board/free/../notice (정상 URL)
실제 차단은 이미 코어가 합니다
코어 dx_path_inside():
realpath() → root boundary 확인
→ 경로 탈출 시 false 반환 → 파일 접근 거부
이 플러그인의 역할:
WAF → 탐지·로그 (공격 시도 모니터링)
dx_pt_safe_path() → PHP Wrapper/Double Encoding 추가 차단
+ 로그 기록
7. 보안 로그 읽는 법
이벤트 타입
| 타입 | 의미 |
|---|---|
PATH:WAF_PATTERN |
Path Traversal 패턴 탐지 |
PATH:WAF_WRAPPER |
PHP Wrapper 패턴 탐지 |
PATH:BLOCKED |
dx_pt_safe_path() 차단 |
PATH:VALIDATE_BLOCKED |
dx_pt_validate_only() 차단 |
PATH:FILENAME_BLOCKED |
파일명 검증 차단 |
PATH:DOWNLOAD_BLOCKED |
다운로드 경로 차단 |
PATH:UPLOAD_FILENAME_BLOCKED |
업로드 파일명 차단 |
PATH:UNUSUAL_FILENAME |
비정상 파일명 경고 |
PATH:BASE_DIR_INVALID |
기준 디렉토리 오류 |
PATH:PARENT_DIR_INVALID |
부모 디렉토리 오류 |
차단 이유 코드
| 이유 코드 | 의미 |
|---|---|
NULL_BYTE |
NULL 바이트 포함 |
PHP_WRAPPER:php:// |
PHP Wrapper 포함 |
ENCODED_TRAVERSAL |
URL 인코딩된 .. |
DOUBLE_ENCODED_TRAVERSAL |
Double 인코딩된 .. |
DOTDOT_SEGMENT |
.. 경로 세그먼트 |
WINDOWS_ABSOLUTE_PATH |
Windows 드라이브 경로 |
UNC_PATH |
UNC 네트워크 경로 |
WINDOWS_DEVICE_NAME:CON |
Windows Device 이름 |
OUTSIDE_ALLOWED_ROOT |
허용 루트 밖 경로 |
PARENT_OUTSIDE_ROOT |
부모 디렉토리가 루트 밖 |
로그 예시
# Path Traversal 시도 탐지
[2026-09-02 09:00:01][PATH:WAF_PATTERN][IP:45.33.x.x][...][UID:0][URI:/api/download]
param=file pattern=/\.\.[\\/]/ val=../../etc/passwd
# PHP Wrapper 시도
[2026-09-02 14:22:10][PATH:WAF_WRAPPER][IP:58.29.x.x][...][UID:5][URI:/api/download]
param=file wrapper=php:// val=php://filter/read=string/resource=../../config.php
# Double Encoding 차단
[2026-09-02 16:45:03][PATH:BLOCKED][IP:103.21.x.x][...][UID:0][URI:/api/upload]
context=file_upload reason=DOUBLE_ENCODED_TRAVERSAL input=%252e%252e%252fconfig.php
# Windows UNC Path 차단
[2026-09-02 20:11:44][PATH:BLOCKED][IP:121.131.x.x][...][UID:1][URI:/admin/file]
context=admin_file reason=UNC_PATH input=\\server\share\evil.php
8. 보안 강점 요약 — 공격 테스트 결과
| 공격 | 코어 | 이 플러그인 |
|---|---|---|
../../../etc/passwd |
✅ 차단 (realpath) | ✅ + 로그 |
..\..\..\config.php |
✅ 차단 | ✅ + 로그 |
%2e%2e%2fpasswd |
✅ 차단 | ✅ + 탐지 로그 |
%252e%252e%252f (Double) |
✅ 차단 | ✅ + 탐지 로그 |
file%00.php (NULL Byte) |
✅ 차단 | ✅ + 탐지 로그 |
php://filter/resource=.. |
✅ 차단 | ✅ + 탐지 로그 |
phar://uploads/evil.zip |
✅ 차단 | ✅ + 탐지 로그 |
C:\Windows\System32 |
✅ 차단 | ✅ + 탐지 로그 |
\\server\share (UNC) |
✅ 차단 | ✅ + 탐지 로그 |
| 존재하지 않는 새 파일 경로 | ❌ realpath=false | ✅ 부모 디렉토리 검증 |
| 정상 이미지 업로드 | ✅ 통과 | ✅ 통과 (영향 없음) |
| 정상 파일 다운로드 | ✅ 통과 | ✅ 통과 (영향 없음) |
9. 개발자 API 가이드
파일 읽기 경로 검증
// 사용자가 제공한 파일명으로 파일을 읽을 때
$safePath = dx_pt_safe_path(
DX_DATA . '/uploads', // 허용 기준 디렉토리
$_GET['filename'], // 사용자 입력
'my_plugin' // 로그 컨텍스트
);
if ($safePath === false) {
http_response_code(400);
exit('허용되지 않는 파일 경로입니다.');
}
// 안전하게 파일 접근
readfile($safePath);
새 파일 생성 경로 검증
// 새 파일을 저장할 경로 검증 (파일이 아직 없음)
$filename = 'report_' . date('Ymd') . '.pdf';
$safePath = dx_pt_safe_path(
DX_DATA . '/reports',
$filename,
'report_gen'
);
if ($safePath === false) {
exit('잘못된 파일 경로입니다.');
}
file_put_contents($safePath, $pdfContent);
파일명만 검증 (경로 없이)
// 파일명만 빠르게 검증
if (!dx_pt_validate_only($_POST['filename'], 'form_input')) {
exit('허용되지 않는 파일명입니다.');
}
안전한 파일명 추출
// 사용자 파일명에서 경로 구분자 제거 후 안전한 파일명 반환
$safeName = dx_pt_safe_filename($_FILES['upload']['name'], 'upload');
if ($safeName === false) {
exit('허용되지 않는 파일명입니다.');
}
// $safeName: basename()만 남은 안전한 파일명
10. 자주 묻는 질문
Q. WAF가 요청을 차단하지 않으면 보안에 문제가 없나요?
코어의 dx_path_inside() + realpath()가 실제 파일 접근 시점에 경로 탈출을 차단합니다. WAF는 공격 시도를 모니터링하는 역할입니다. WAF에서 미리 차단하면 게시물 내용에 경로 문자열이 있을 때 정상 요청도 차단되는 오탐이 발생합니다.
Q. 플러그인을 설치하면 기존 파일 업로드/다운로드에 영향이 있나요?
없습니다. 이 플러그인은 코어 파일을 수정하지 않습니다. dx_pt_safe_path() API는 플러그인 개발자가 직접 호출할 때만 동작합니다. WAF는 탐지·로그만 수행하고 요청을 차단하지 않습니다.
Q. 코어 dx_path_inside()를 대체해야 하나요?
아닙니다. 코어 dx_path_inside()는 잘 설계되어 있습니다. 이 플러그인의 dx_pt_safe_path()는 새로 작성하는 플러그인 코드에서 사용하는 보강 API입니다. 코어 코드는 그대로 사용하면 됩니다.
DX Security — Path Traversal 방어 v1.0.0 — DesignOneX
https://designonex.com
라이선스
디자인원엑스 라이선스 참고
https://designonex.com/notice/view/1787577858493247