한양과학기술고등학교

에듀테크 프로젝트 대시보드

경로 탐색 공격 (Path Traversal)

🗂️ 가상 파일 서버 /download?file=
📁 /var/www/uploads/ (웹루트, BASE_DIR)
├─ 📄 notice.txt
├─ 📄 logo.png
└─ 📄 report.pdf
📁 /var/www/app/ (웹루트 밖)
└─ 🔒 config.py ← SECRET_KEY
📁 /etc/
├─ 🔒 passwd
└─ 🔒 shadow
🧭 경로 정규화 과정
① BASE_DIR + 입력을 이어붙인 원본 경로
—
② 세그먼트 분해 → ..는 직전 폴더 삭제
③ 최종 정규화 경로 (실제로 열리는 파일)
—
파일명을 입력하고 "요청 보내기"를 눌러보세요.
🐍 FastAPI 서버 코드 main.py
BASE_DIR = Path("/var/www/uploads").resolve() @app.get("/download") def download(file: str): # ✅ 실제 경로로 정규화 후 접두사 검사 path = (BASE_DIR / file).resolve() if not path.is_relative_to(BASE_DIR): raise HTTPException(403, "Forbidden") return FileResponse(path)
📋 서버 요청 로그 실시간
// 서버 대기 중...

📊 요청 현황
정상 다운로드 0
웹루트 탈출 성공 0
403 차단 0
⚡ 경로 탈출 공격 흐름
① file=notice.txt 요청
→
② 웹루트 안 파일, 정상
↓
③ file=../../../etc/passwd
→
④ BASE_DIR과 이어붙임
↓
⑤ ".." 가 상위 폴더 3번 이탈
→
⑥ 웹루트 밖 파일 그대로 반환 💀
핵심 원인: 파일시스템은 ..를 "부모 디렉터리"로 정상 해석합니다. 서버가 합쳐진 경로를 열기 전에 웹루트 안에 있는지 확인하지 않으면, 사용자가 입력하는 경로만으로 임의 파일을 읽게 됩니다.
🛡️ 방어 방법 3가지
① 정규화 + 접두사 검사
경로를 실제 절대경로로 정규화한 뒤, 결과가 BASE_DIR로 시작하는지 확인. 문자열 치환보다 우회 불가능한 근본적 방어.

② 화이트리스트 매칭
파일명을 자유 입력 대신 DB의 파일 ID나 정해진 목록에서만 선택하도록 설계. 애초에 경로 조합 자체를 없앰.

③ 샌드박스 / chroot
프로세스가 볼 수 있는 파일시스템 범위 자체를 OS 레벨에서 제한. 컨테이너(Docker)도 같은 원리로 호스트 파일시스템을 격리.
주의: "../"를 1회만 치환하는 필터는 "....//" 같은 입력을 넣으면 치환 후 "../"가 재조합되어 우회됩니다. "부실한 필터" 모드로 직접 확인해 보세요.