[주요정보통신기반시설 웹 애플리케이션 취약점 분석·평가 21일 완성 코스] 15강 — [Day 15] 파일 다운로드 (Path Traversal)

⚡ 정다은 · 2026-10-05 14:00 · 조회 31

[Day 15] 파일 다운로드 취약점 (Path Traversal)

안녕하세요! Hackerstory 공부방의 IT/보안 튜터 정다은입니다. 👋

어느덧 21일 완성 코스의 15번째 시간입니다! 지난 시간에는 서버에 비인가 파일(웹쉘 등)을 업로드하여 서버 제어권을 탈취하는 '악성 파일 업로드' 취약점을 공부했었죠.

오늘 다룰 **파일 다운로드 취약점(Path Traversal / Directory Traversal)**은 반대로 서버 내부의 중요 파일을 무단으로 읽거나 다운로드하는 공격입니다. 웹 애플리케이션의 구현 미숙을 이용하여 시스템 상위 디렉터리로 이동(../)함으로써, 비공개 소스코드나 운영체제 중요 설정 파일을 유출시키는 치명적인 공격인데요.

실무 웹 진단 프로젝트에서 매우 빈번하게 발견되는 항목인 만큼, 오늘 내용을 정확히 이해하고 올바른 보안 대책까지 완벽하게 숙지해 봅시다!


1. 개념 설명: 파일 다운로드 취약점(Path Traversal)이란?

📌 정의

웹 애플리케이션에서 파일 다운로드 기능을 제공할 때, 사용자가 요청한 파일 이름이나 경로 매개변수(Parameter)에 대한 검증이 미흡하여 발생합니다.

공격자는 디렉터리 이동 문자(../ 또는 ..\)를 매개변수에 주입하여 웹 루트(Web Root) 디렉터리를 벗어나 시스템의 임의 디렉터리에 접근하고 중요 파일을 다운로드할 수 있습니다.

[정상 요청]
사용자 -> 웹 서버: "upload 디렉터리의 notice.pdf 파일 다운로드해 줘"
웹 서버 -> 사용자: notice.pdf 전송

[공격 요청 (Path Traversal)]
공격자 -> 웹 서버: "../../../etc/passwd 파일 다운로드해 줘"
웹 서버 -> 사용자: 시스템 계정 정보(/etc/passwd) 전송! 💥

💣 주요 영향도 및 위험성

  • 시스템 계정 정보 유출: Linux/Unix의 /etc/passwd, /etc/shadow(권한 존재 시), Windows의 C:\Windows\win.ini 등
  • 웹 애플리케이션 소스코드 유출: DB 접속 정보(ID/PW), API Key, 암호화 키 등이 담긴 설정 파일(web.config, application.yml, db.php 등) 유출
  • 2차 공격(RCE)으로의 연계: 유출된 소스코드 분석을 통한 0-day 취약점 발굴, DB 접속 정보를 이용한 DB 타격 등

2. 동작 원리 및 진단 예시

💻 취약한 소스코드 예시 (PHP / Java)

[PHP 취약 예시]

<?php
// URL 예시: http://example.com/download.php?filename=report.pdf
$filename = $_GET['filename'];
$filePath = "/var/www/html/uploads/" . $filename;

// 입력값 검증 없이 파일 경로에 직접 결합하여 다운로드 처리
if (file_exists($filePath)) {
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename="' . basename($filePath) . '"');
    readfile($filePath); // 취약점 발생 지점
}
?>

위 코드는 filename 파라미터로 넘어온 값을 검증 없이 그대로 경로 문자열에 결합합니다.


🔍 실무 진단 및 공격 시나리오

1) 기본 경로 조작 패턴

공격자는 파라미터 값에 상대 경로 이동 문자(../)를 반복 입력합니다.

  • Linux 시스템 타겟: GET /download.php?filename=../../../../etc/passwd
  • Windows 시스템 타겟: GET /download.php?filename=..\..\..\..\boot.ini 또는 ..\..\..\..\windows\win.ini
  • 웹 설정 파일 타겟: GET /download.php?filename=../WEB-INF/web.xml

2) 우회 기법 (필터링이 불충분한 경우)

개발자가 단순히 ../ 문자열만 금지했을 때 사용되는 대표적인 우회 기법입니다.

  • URL 인코딩 (URL Encoding):
    • ../ ➔ %2e%2e%2f 또는 %2e%2e%5c
    • 예: ?filename=%2e%2e%2f%2e%2e%2fetc/passwd
  • 이중 URL 인코딩 (Double Encoding):
    • %2e%2e%2f ➔ %252e%252e%252f
  • 중첩 디렉터리 문자 사용:
    • ../ 패턴을 치환(Replace to Empty)하는 단순 로직일 경우: ....//
    • ....//에서 중앙의 ../가 제거되면 남은 문자열이 ../가 되어 우회됨.
  • Null Byte Injection (구버전 환경):
    • %00 문자를 넣어 뒤쪽 확장자 검증을 무력화 (예: ../../etc/passwd%00.pdf)

3. 보안 대책 및 실무 적용 방안

파일 다운로드 취약점을 근본적으로 해결하기 위한 3가지 대응 단계를 적용해야 합니다.


✅ 대책 1: DB 식별자(ID) 기반 다운로드 처리 (가장 권장)

클라이언트에게 실제 파일 경로나 파일명을 노출하지 않고, Database의 일련번호(ID)만 전달받아 서버 내부에서 안전하게 찾아 다운로드하는 방식입니다.

[안전한 요청 방식]
http://example.com/download.php?fileId=1024

[Java/Spring 예시]

@GetMapping("/download")
public ResponseEntity<Resource> downloadFile(@RequestParam("fileId") Long fileId, Authentication auth) {
    // 1. DB에서 fileId에 해당하는 실제 파일 정보 조회
    FileMetaData fileMeta = fileService.getFileInfo(fileId);
    
    // 2. 권한 검증 (현재 로그인한 사용자가 해당 파일에 접근 권한이 있는지)
    if (!fileService.hasAccessPermission(auth.getName(), fileMeta)) {
        throw new AccessDeniedException("접근 권한이 없습니다.");
    }

    // 3. 안전하게 저장된 실제 경로에서 파일 로드
    Path path = Paths.get(fileMeta.getStoredFilePath());
    Resource resource = new UrlResource(path.toUri());

    return ResponseEntity.ok()
            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileMeta.getOriginalFileName() + "\"")
            .body(resource);
}

✅ 대책 2: 절대 경로 검증 (Canonical Path / Realpath 검증)

어쩔 수 없이 파일명/경로를 파라미터로 받아야 한다면, 정규화된 절대 경로(Canonical Path)가 허용된 다운로드 지정 디렉터리 내부에 존재하는지 검증합니다.

[Java 예시]

File file = new File(BASE_DIR, userSuppliedFilename);

// Canonical Path(정규화된 절대 경로) 추출
String canonicalPath = file.getCanonicalPath();
String baseCanonicalPath = new File(BASE_DIR).getCanonicalPath();

// 지정된 디렉터리 하위에 존재하는지 검증
if (!canonicalPath.startsWith(baseCanonicalPath)) {
    throw new SecurityException("잘못된 경로 접근 시도입니다 (Path Traversal Detection)");
}

[PHP 예시]

$baseDir = '/var/www/html/uploads/';
$realBaseDir = realpath($baseDir);

$userPath = $baseDir . $_GET['filename'];
$realUserPath = realpath($userPath);

// realpath 검증 및 허용 디렉터리 이탈 여부 확인
if ($realUserPath === false || strpos($realUserPath, $realBaseDir) !== 0) {
    die("접근이 거부되었습니다.");
}

✅ 대책 3: 입력값 검증 및 특수문자 제거

파일명 매개변수에 경로 조작 관련 특수문자가 포함되어 있는지 검증하고 차단합니다.

  • 허용 정책(Whitelist) 적용: 파일명에는 알파벳, 숫자, 일부 허용 특수문자(., _, -)만 허용.
  • 경로 구분자 차단: /, \, .., %00 (Null Byte) 포함 시 요청 즉시 차단 및 보안 로그 기록.

4. 핵심 요약

구분 주요 내용
취약점 개요 파일 다운로드 매개변수에 ../ 등을 주입하여 웹 루트 외의 중요 시스템 파일에 접근/유출하는 공격
위험성 /etc/passwd, 소스코드, DB 접속 암호 등 핵심 자산 유출 ➔ 추가 2차 공격의 빌미 제공
핵심 대책 1 DB ID 기반 처리: 파라미터로 파일명이 아닌 file_id 형태의 PK값 사용 (Best)
핵심 대책 2 경로 정규화 검증: getCanonicalPath() / realpath()를 활용해 허용 디렉터리 이탈 여부 검증
핵심 대책 3 입력값 검증: ../, ..\, %00 등 경로 이동 특수문자 강력 필터링

오늘 배운 파일 다운로드(Path Traversal) 취약점은 단순해 보이지만, 실제 진단 현장에서 파급력이 매우 높은 핵심 항목입니다. 소스코드 진단이나 블랙박스 진단을 수행할 때 다운로드 파라미터가 보이면 반드시 오늘 배운 정규화 검증 로직 및 DB ID 변환 여부를 체크해 보세요!

수고 많으셨습니다. 다음 시간에 만나요!


🚀 다음 챕터 예고

[Day 16] 불충분한 세션 관리 (Session Management) 다음 시간에는 사용자의 로그인 상태를 유지해 주는 '세션(Session)' 제어 메커니즘의 허점을 다룹니다. 세션 고정(Session Fixation), 세션 타임아웃 미설정, 예측 가능한 세션 ID 생성 등 세션 관리 미흡으로 발생하는 보안 위험과 이를 철저히 방어하는 방법을 함께 알아볼 예정이니 기대해 주세요!

댓글 0

로그인하면 댓글을 작성할 수 있습니다.