FlawOpen

flawopen.com/경로 순회/Zip Slip

Zip Slip 취약점이란?

Reference page
ELI5

ZIP 파일에는 ../../outside.txt 같은 이름이 들어갈 수 있습니다. 압축 해제기가 이를 그대로 신뢰하면 프로세스 권한 범위에서 대상 폴더 밖에 파일을 쓸 수 있습니다.

놓치기 쉬운 이유

압축 해제는 평범한 작업처럼 보이지만 신뢰할 수 없는 ZIP 또는 TAR의 각 항목 이름은 파일 작성자가 제어합니다. 경로 검증 없이 outputDir + entry.name을 결합하면 아카이브 내용을 통해 디렉터리 밖으로 나갈 수 있습니다.

VULNERABLE (conceptual)
for entry in zip_file:
    dest = os.path.join(output_dir, entry.name)
    write_file(dest, entry.read())
# entry.name은 "../../outside.txt"일 수 있음
어휘적 경로 포함 검사만 수행 — 단독으로 경쟁 상태를 막지 못함
root = os.path.realpath(output_dir)
os.makedirs(root, exist_ok=True)
for entry in zip_file:
    dest = os.path.realpath(
      os.path.join(root, entry.name)
    )
    try:
        inside_root = os.path.commonpath([root, dest]) == root
    except ValueError:
        inside_root = False
    if not inside_root:
        raise ValueError("Unsafe entry path")
    write_file(dest, entry.read())

이 검사는 문자열 접두사만 같은 인접 경로를 포함해 루트 밖으로 나가는 경로를 차단합니다. 새 비공개 디렉터리에 압축을 풀고 symlink 항목을 거부하세요. 기존 symlink와 동시 파일 시스템 변경까지 막으려면 경쟁 조건에 안전한 API나 유지 관리되는 추출 라이브러리가 필요합니다.

자주 묻는 질문

Python 아카이브 API마다 경로 보호가 다릅니다

위 코드 쌍은 사용자 정의 추출 루프의 개념 예시이며 Python 내장 ZipFile.extractall()이 아닙니다. Python 문서에 따르면 ZipFile.extract()와 extractall()은 절대 경로와 ..로 인한 디렉터리 탈출을 막으려 하지만, 신뢰할 수 없는 아카이브는 먼저 검사하라고 경고합니다. zipfile.Path는 멤버 이름을 정리하지 않으므로 호출 코드가 직접 검증해야 합니다. TAR 추출 필터는 Python 3.12에서 추가되었고 Python 3.14부터 기본값이 filter="data"로 바뀌었습니다. 기본값이 덜 제한적일 수 있는 지원 런타임에서는 이 필터를 명시적으로 지정하세요. data 필터도 모든 위험 동작이나 리소스 고갈을 막지는 않습니다. 새 비공개 임시 디렉터리에 추출하고 아카이브 크기, 멤버 수, 총 압축 해제 바이트, 처리 시간을 제한하세요.

최신 아카이브 라이브러리는 자동으로 막아 주나요?

2018년에 이 패턴이 널리 알려진 뒤 일부 버전에 보호 기능이 추가됐지만, 기본적으로 경로를 검증하지 않는 라이브러리도 많습니다. 사용 중인 버전의 문서를 확인하고 모든 항목을 명시적으로 검증하세요.

참고 자료

함께 보기 경로 조작(CWE-22): 동작 원리와 모든 언어에서의 예방법 — flawopen.com Node.js path.join()은 Path Traversal을 막지 못합니다 PHP include()와 사용자 제어 경로 Python os.path.join() 경로 탐색 수정