Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 706 – tarfile.extractall 필터

Author:
Petr Viktorin <encukou at gmail.com>
Discussions-To:
Discourse thread
Status:
Final
Type:
Standards Track
Created:
09-Feb-2023
Python-Version:
3.12
Post-History:
25-Jan-2023, 15-Feb-2023
Resolution:
Discourse message

Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain or CC0-1.0, whichever is more permissive 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

Important

This PEP is a historical document. The up-to-date, canonical documentation can now be found at tarfile documentation.

×

See PEP 1 for how to propose changes.

초록

filter인자를 tarfile의 추출 메서드에 추가하여, 아카이브를 추출하는 동안 파일을 거부하거나 메타데이터를 수정할 수 있도록 합니다. 놀랍거나 위험할 수 있는 기능을 제한하기 위한 이름이 지정된 내장 필터 세 가지가 제공됩니다. 이를 그대로 사용하거나 사용자 지정 필터의 기반으로 사용할 수 있습니다.

사용 중단 기간이 지나면 엄격하지만 더 안전한 필터가 기본값이 됩니다.

동기

tar형식은 여러 사용 사례에 사용되며, 그중 많은 경우에 서로 다른 요구 사항이 있습니다. 예를 들면 다음과 같습니다.

  • UNIX 워크스테이션의 백업은 파일 권한, 시스템 구성에 대한 심볼릭 링크, 다양한 종류의 특수 파일과 같은 모든 세부 사항을 충실하게 보존해야 합니다.
  • 데이터 번들을 압축 해제할 때는 공개된 장소에 비밀번호 파일을 심볼릭 링크하여 노출하는 것과 같은 의도하지 않은 결과가 압축 해제 과정에서 발생하지 않는 것이 훨씬 중요합니다.

모든 사용 사례를 지원하기 위해 tar형식에는 많은 기능이 있습니다. 많은 경우 아카이브를 추출할 때 이러한 기능 중 일부를 무시하거나 허용하지 않는 것이 가장 좋습니다.

Python에서는 tarfile.TarFile.extractall()을 사용하여 tar아카이브를 추출할 수 있으며, 해당 문서에서는 신뢰할 수 없는 출처의 아카이브를 사전 검사 없이 절대로 추출하지 마십시오라고 경고합니다. 그러나 어떤 종류의 검사를 수행해야 하는지는 명확하지 않습니다. 실제로 이러한 검사를 올바르게 수행하는 일은 상당히 까다롭습니다. 그 결과 많은 사람이 검사를 하지 않거나 잘못 수행하며, CVE-2007-4559와 같은 보안 문제가 발생합니다.

처음 tarfile이 작성된 이후로, 문서의 경고만으로는 충분하지 않다는 인식이 더욱 널리 받아들여졌습니다. 가능한 경우 안전하지 않은 작업은 명시적으로 요청되어야 하며, 잠재적으로 위험한 작업은 위험해 보이도록 해야 합니다. 그러나 코드 검토에서 TarFile.extractall은 무해해 보입니다.

tarfile 추출은 shutil.unpack_archive()를 통해서도 제공되며, 사용자가 다루는 아카이브의 종류를 신경 쓰지 않아도 되도록 합니다. 문서에서 다시 사용하지 말라고 경고하고 있음에도 이 API는 사전 검사 없이 아카이브를 추출하도록 매우 쉽게 유도합니다.

Python이 잘못한 것은 아니며 문서에 명시된 대로 정확히 동작한다는 주장이 제기되었지만, 그것은 핵심에서 벗어난 이야기입니다. 책임을 묻거나 회피하기보다는 상황을 개선합시다. Python과 해당 문서는 이러한 사항을 개선하기에 가장 적합한 곳입니다.

근거

어떻게 개선해야 합니까? 안타깝게도 기본값을 변경해야 하며, 이는 하위 호환성을 깨뜨리는 것을 의미합니다. TarFile.extractall은 사람들이 tarball을 추출해야 할 때 사용하는 방법입니다. 기본 동작을 변경해야 합니다.

최선의 동작은 무엇입니까? 사용 사례에 따라 다릅니다. 그러므로 추출을 제어하기 위한 몇 가지 일반적인 “정책”을 추가합니다. 이는 사용 사례를 기반으로 하며, 이상적으로는 보안상의 의미가 명확해야 합니다:

  • 현재 동작: 아카이브를 신뢰합니다. 예를 들어 검사를 직접 수행하는 라이브러리의 구성 요소로 사용하거나, 방금 직접 만든 아카이브를 추출하는 데 적합합니다.
  • UNIX 아카이브 압축 풀기: GNU tar를 대략 따르며, 예를 들어 파일 이름에서 앞쪽 /를 제거합니다.
  • 일반 데이터 아카이브 압축 풀기: tar 또는 유닉스 계열 파일 시스템에 특화된 세부 사항을 보존하는 것이 중요하지 않은 shutil.unpack_archive() 사용 사례입니다.

지원 중단 기간이 지나면 마지막 옵션, 즉 가장 제한적이지만 가장 안전한 옵션이 기본값이 됩니다.

더 나은 일반 기본값을 사용하더라도 사용자는 추출하는 아카이브를 여전히 검증하고, 메타데이터 일부를 수정할 수도 있어야 합니다. 피상적으로 보면 오늘날 이를 수행하는 합리적인 방법은 다음과 같습니다:

  • 다음 메서드를 호출하십시오: TarFile.getmembers
  • 각 멤버의 TarInfo를 검증하거나 수정합니다.
  • 결과를 extractallmembers에 전달합니다.

그러나 이 접근 방식에는 몇 가지 문제가 있습니다:

  • TarInfo 객체를 수정할 수 있지만, 해당 객체의 변경 사항은 같은 TarFile 객체에 대한 이후의 모든 작업에 영향을 미칩니다. 이 동작은 대부분의 용도에 적합하지만, 그럼에도 TarFile.extractall이 기본적으로 이렇게 동작한다면 매우 놀라운 일일 것입니다.
  • getmembers를 호출하는 것은 비용이 많이 들 수 있으며, requires a seekable archive는 검색 가능한 아카이브가 필요하다는 뜻입니다.
  • 사전에 멤버를 검증할 때는 각 멤버가 파일 시스템을 어떻게 변경했을지, 예를 들어 심볼릭 링크가 어떻게 설정되는지를 추적해야 할 수 있습니다. 이는 어렵습니다. 사용자가 이를 수행하리라고 기대할 수 없습니다.

이러한 문제를 해결하기 위해 다음을 수행합니다:

  • TarInfo 객체를 “복제”하고 수정할 수 있는 지원되는 방법을 제공합니다. replace 메서드는 dataclasses.replace() 또는 namedtuple._replace와 유사한 방식으로 동작해야 합니다.
  • extractall의 루프에 처리 전에 멤버를 수정하거나 버릴 수 있는 “필터” 후크를 제공합니다.
  • 이 후크가 각 멤버를 추출하기 직전에 호출되도록 하여, 디스크의 현재 상태를 검사할 수 있게 합니다. 이렇게 하면 정확한 “시험 실행”을 수행할 수 없게 되는 대신, 정책 구현이 크게 간소화됩니다(표준 라이브러리와 사용자 코드 모두에서).

후크 API는 TarFile.add의 기존 filter 인자와 매우 유사합니다. 또한 이를 filter라고 명명합니다. (일부 경우에는 “정책”이 더 적합한 이름이지만, 이 API는 보안 정책 외에도 사용될 수 있습니다.)

위에서 설명한 내장 정책/필터는 공개 필터 API를 사용하여 구현되므로, 구성 요소나 예제로 사용할 수 있습니다.

선례 설정

아카이브 추출을 위한 다른 라이브러리(예: zipfile)가 유사한 기능을 갖추게 된다면, 합리적인 범위에서 이 API를 모방해야 합니다.

간단한 경우에도 이를 사용할 수 있도록 내장 필터에는 문자열 이름이 지정됩니다. 예를 들어 사용자는 TarInfo 객체를 처리하는 특정 함수 대신 filter='data'를 전달할 수 있습니다.

관련 shutil.unpack_archive() 함수에는 filter 인자가 추가되며, 이 인자를 extractall에 전달합니다.

아카이브 형식 전반에서 작동하는 함수 기반 API를 추가하는 것은 이 PEP의 범위에 포함되지 않습니다.

완전한 공개 및 재배포자 정보

이 PEP의 작성자는 일반적인 CPython과는 보안 요구 사항과 지원 기간이 다른 Python 재배포자인 Red Hat에서 근무합니다. 이러한 재배포자는 다음과 같은 공급업체 패치를 유지하려 할 수 있습니다:

  • 시스템 전체에서 기본값을 구성할 수 있도록 합니다.
  • 더 오래된 Python 버전에서도 가능한 한 빨리 기본값을 변경합니다.

이 제안은 이를 쉽게 수행할 수 있도록 하며, 사용자가 설정을 조회할 수 있도록 합니다.

사양

멤버 메타데이터 수정 및 삭제

관련 TarInfo 클래스에는 dataclasses.replace와 유사하게 작동하는 새로운 메서드인 replace()가 추가됩니다. 이 메서드는 키워드 전용 인자로 지정한 대로 속성이 대체된 TarInfo객체의 복사본을 반환합니다:

  • name
  • mtime
  • mode
  • linkname
  • uid
  • gid
  • uname
  • gname

namelinkname을 제외한 이들 항목은 모두 None으로 설정할 수 있습니다. extract 또는 extractall이 그러한 None을 만나면 해당 메타데이터를 설정하지 않습니다. (uname 또는 gnameNone이면 이름을 찾지 못한 경우처럼 uid 또는 gid로 대체됩니다.) addfile 또는 tobuf가 그러한 None을 만나면 ValueError를 발생시킵니다. list가 그러한 None을 만나면 자리 표시자 문자열을 출력합니다.

문서에서는 이 메서드가 존재하는 이유를 설명합니다. TarFile.getmembers에서 가져온 TarInfo객체는 “라이브” 상태이므로, 이를 직접 수정하면 이후의 관련 없는 작업에 영향을 줍니다.

필터

TarFile.extractTarFile.extractall 메서드에는 filter 키워드 전용 인자가 추가되며, 이 인자는 다음과 같이 호출할 수 있는 호출 가능 객체를 받습니다.:

filter(/, member: TarInfo, path: str) -> TarInfo|None

여기서 member는 추출할 멤버이고, path는 아카이브가 추출되는 경로입니다(즉, 모든 멤버에 대해 동일합니다).

이 인자를 사용하면 추출되는 각 멤버에 대해 호출되며, 추출은 그 결과를 사용하여 수행됩니다. 결과가 None이면 해당 멤버를 건너뜁니다.

이 함수는 예외를 발생시킬 수도 있습니다. 이는 TarFile.errorlevel에 따라 추출을 중단하거나 해당 멤버를 건너뛰게 할 수 있습니다.

Note

추출이 중단되면 아카이브가 부분적으로만 추출된 상태로 남을 수 있습니다. 정리 작업은 사용자의 책임입니다.

또한 일반적인 사용 사례를 위한 기본값 집합도 제공합니다. 함수 외에도 filter인자는 다음 문자열 중 하나일 수 있습니다.

  • 'fully_trusted': 현재 동작으로, 메타데이터를 있는 그대로 따릅니다. 사용자가 아카이브를 완전히 신뢰하거나 자체적으로 복잡한 검증을 구현한 경우 사용해야 합니다.
  • 'tar': 일반 사용자로 실행할 때 GNU tar 명령의 기본값을 대략적으로 따릅니다.
    • 파일 이름에서 앞쪽의 '/'os.sep를 제거합니다.
    • 위에서 /를 제거한 후 절대 경로를 가진 파일(예: Windows의 C:/foo)은 추출하지 않습니다.
    • 심볼릭 링크를 따른 후 절대 경로가 대상 디렉터리 외부에 위치하게 되는 파일은 추출하지 않습니다. (GNU tar는 대신 일부 링크의 생성을 지연합니다.)
    • 높은 모드 비트(setuid, setgid, sticky)와 그룹/기타 쓰기 비트(S_IWGRP|S_IWOTH)를 제거합니다. (이는 현재 umask 설정에 따라 모드를 제한하는 GNU tar의 기본 동작을 근사한 것입니다.)
  • 'data': 일반적인 공격 벡터를 차단하지만 기능을 제한하는 “데이터” 아카이브를 추출합니다. 특히 UNIX 스타일 파일 시스템(또는 이에 상응하는 tar 아카이브 형식)에 특화된 많은 기능을 무시하므로, 플랫폼 간 아카이브에 적합한 필터입니다. tar에 더해 다음을 적용합니다.
    • 절대 경로를 가리키는 링크(하드 링크 또는 소프트 링크)는 추출하지 않습니다.
    • 대상 디렉터리 외부의 경로를 가리키게 되는 링크(하드 링크 또는 소프트 링크)는 추출하지 않습니다. (링크를 지원하지 않는 시스템에서는 tarfile이 대부분의 경우 일반 파일을 생성하는 방식으로 대체합니다. 이 제안은 해당 동작을 변경하지 않습니다.)
    • 장치 파일(파이프 포함)은 추출하지 않습니다.
    • 일반 파일 및 하드 링크의 경우 다음을 적용합니다.
      • 소유자의 읽기 및 쓰기 권한(S_IRUSR|S_IWUSR)을 설정합니다.
      • 소유자에게 실행 권한(S_IXUSR)이 없는 경우 그룹 및 기타 사용자의 실행 권한(S_IXGRP|S_IXOTH)을 제거합니다.
    • 기타 파일(디렉터리)의 경우 모드를 완전히 무시하고 None으로 설정합니다.
    • 사용자 및 그룹 정보는 무시하고 uid, gid, uname, gnameNone으로 설정합니다.

그 밖의 문자열은 ValueError를 발생시킵니다.

해당 필터 함수는 tarfile.fully_trusted_filter(), tarfile.tar_filter() 등으로 제공되므로 사용자 지정 정책에서 쉽게 사용할 수 있습니다.

이러한 필터는 절대로 None을 반환하지 않는다는 점에 유의하십시오. 이러한 방식으로 멤버를 건너뛰는 것은 사용자 정의 필터를 위한 기능입니다.

기본값 및 구성

TarFile에 기본 필터를 구성할 수 있도록 새 속성인 extraction_filter가 추가됩니다. 기본값은 None이지만, 사용자는 filter 인자가 없거나 None일 때 사용될 호출 가능 객체를 설정할 수 있습니다.

Note

문자열 이름은 여기서 허용되지 않습니다. 그러면 my_tarfile.extraction_filter = 'data'와 같은 코드를 사용하도록 조장하게 됩니다. 이 기능이 없는 Python 버전에서는 아무 작업도 수행하지 않아, 보안 관련 요청을 조용히 무시하게 됩니다.

인자와 속성이 모두 None일 때:

  • Python 3.12-3.13에서는 DeprecationWarning이 발생하고 추출에 'fully_trusted' 필터를 사용합니다.
  • Python 3.14 이상에서는 'data' 필터를 사용합니다.

애플리케이션과 시스템 통합자는 전역 기본값을 설정하기 위해 TarFile 클래스 자체의 extraction_filter를 변경할 수 있습니다. 함수를 사용할 때는 일반적으로 self 인자의 주입을 방지하기 위해 해당 함수를 staticmethod()로 감싸려고 할 것입니다.

TarFile의 서브클래스는 extraction_filter를 재정의할 수도 있습니다.

FilterError

새로운 예외인 FilterErrortarfile 모듈에 추가됩니다. 여기에는 위의 각 거부 사유에 해당하는 여러 새로운 서브클래스가 포함됩니다. FilterErrormember속성에는 관련 TarInfo가 포함됩니다.

위 목록에서 파일 추출을 “거부한다”는 것은 FilterError가 발생한다는 의미입니다. 다른 추출 오류와 마찬가지로 TarFile.errorlevel이 1 이상이면 추출이 중단됩니다. errorlevel=0일 때는 오류가 기록되고 멤버가 무시되지만 추출은 계속됩니다. extractall()은 아카이브를 부분적으로 추출한 상태로 남길 수 있다는 점에 유의하십시오. 정리 작업은 사용자의 책임입니다.

오류 수준 및 치명적/비치명적 오류

현재 TarFile에는 errorlevel 인자/속성이 있으며, 이는 오류 처리 방식을 지정합니다:

  • errorlevel=0일 때 문서에서는 extract()extractall() 사용 시 “모든 오류가 무시된다”고 설명합니다. 코드는 비치명적 오류와 치명적 오류만 무시하므로(아래 참조), 예를 들어 대상 경로로 None을 전달하면 여전히 TypeError가 발생합니다.
  • errorlevel=1(기본값)일 때 모든 비치명적 오류가 무시됩니다. (debug 인자/속성을 설정하면 해당 오류가 sys.stderr에 기록될 수 있습니다.) 어떤 오류가 비치명적인지는 문서에 정의되어 있지 않지만, 코드에서는 ExtractionError를 이러한 오류로 처리합니다. 구체적으로 다음 문제들입니다:
    • “아카이브 내부의 링크를 확인할 수 없음”(심볼릭 링크를 지원하지 않는 시스템에서 발생)
    • “FIFO/특수 장치를 시스템에서 지원하지 않음”(시스템이 이를 지원하는 경우, 예를 들어 PermissionError와 같은 실패에는 사용되지 않음)
    • “소유자/모드/수정 시간을 변경할 수 없음”

    예를 들어 파일 이름이 너무 긺 또는 디스크 공간 부족은 해당하지 않는다는 점에 유의하십시오. 비치명적 오류는 Unix 계열 시스템에서 발생할 가능성이 높지 않습니다.

  • errorlevel=2일 때는 치명적 오류를 포함한 모든 오류가 발생합니다. 어떤 오류가 치명적인지는 역시 정의되어 있지 않으며, 실제로는 OSError입니다.

필터가 멤버 추출을 거부하는 경우는 치명적/비치명적 범주에 깔끔하게 들어맞지 않습니다.

  • 이 PEP는 기존 동작을 변경하지 않습니다. 개선 아이디어는 Discourse topic 25970에서 환영합니다.
  • 필터가 멤버 추출을 거부할 때 오류가 기본적으로 조용히 무시되어서는 안 됩니다.

이를 충족하기 위해 FilterError치명적 오류로 간주되며, 즉 errorlevel=0일 때만 무시됩니다.

FilterError를 무시하고 다른 fatal 오류는 무시하지 않으려는 사용자는 사용자 지정 필터 함수를 만들고, try 블록에서 다른 필터를 호출해야 합니다.

추가 검증을 위한 참고 사항

제안된 변경 사항을 적용하더라도, 사전 검사 없이 신뢰할 수 없는 파일을 추출하는 데 tarfile은 적합하지 않습니다. 다른 문제 중에서도 제안된 정책은 서비스 거부 공격을 방지하지 못합니다. 사용자는 추가 검사를 수행해야 합니다.

새 문서에서는 다음 사항을 고려하도록 안내합니다.

  • 새 빈 디렉터리로 추출하기
  • 디스크, 메모리 및 CPU 사용량에 외부(예: 운영 체제 수준의) 제한 사용하기
  • 제어 문자, 혼동 가능 문자 등을 걸러내기 위해 허용된 문자 목록과 파일 이름 대조하기
  • 파일 이름에 예상되는 확장자가 있는지 확인하기(“파일을 클릭하면” 실행되는 파일이나 Windows 특수 장치 이름과 같은 확장자가 없는 파일을 사용하지 않도록 하기)
  • 추출되는 파일 수, 추출된 데이터의 총 크기 및 개별 파일의 크기 제한하기
  • 대소문자를 구분하지 않는 파일 시스템에서 다른 파일에 가려질 파일이 있는지 확인하기

또한 문서에서는 다음 사항을 언급합니다.

  • tar 파일에는 일반적으로 동일한 파일의 여러 버전이 포함됩니다. 추출할 때 나중에 나온 버전이 앞서 나온 버전을 덮어쓰는 것이 일반적입니다.
  • tarfile은 “live” 데이터와 관련된 문제로부터 보호하지 않습니다. 예를 들어 추출(또는 추가) 작업이 진행되는 동안 공격자가 대상 디렉터리를 변조할 수 있습니다(자세한 내용은 GNU tar manual을 참조하십시오).

이 목록이 포괄적이지는 않지만, 문서는 이러한 일반적인 지침을 모아 두기에 적합한 곳입니다. 목록이 너무 길어지거나 zipfile 또는 shutil과 통합해야 하는 경우(이는 이 제안의 범위를 벗어납니다) 별도 문서로 옮길 수 있습니다.

TarInfo 동일성과 offset

replace()를 사용하는 필터를 적용하면 추출 메커니즘이 처리하는 TarInfo 객체가 members에 있는 객체와 반드시 동일하지는 않습니다. 이는 makelink와 같은 메서드를 재정의하고 객체 동일성에 의존하는 TarInfo 서브클래스에 영향을 줄 수 있습니다.

이러한 코드는 파일 내부에서 멤버 헤더가 위치한 지점인 offset을 비교하도록 전환할 수 있습니다.

재정의 가능한 메서드와 offset은 모두 소스 주석에서만 문서화되어 있다는 점에 유의하십시오.

tarfile CLI

CLI(python -m tarfile)에는 제공되는 기본 필터 중 하나의 이름을 받는 --filter 옵션이 추가됩니다. 사용자 지정 필터 함수를 지정할 수는 없습니다.

--filter가 지정되지 않으면 CLI는 기본 필터를 사용합니다('fully_trusted'를 현재는 사용 중단 경고와 함께 사용하고, Python 3.14부터는 'data'를 사용합니다).

짧은 옵션은 제공되지 않습니다.(-f는 GNU tar의 파일 이름 옵션과 혼동하기 쉽습니다.)

기타 아카이브 라이브러리

관련 zipfile 같은 다른 아카이브 라이브러리에 유사한 기능이 추가된다면, 해당 라이브러리의 추출 함수는 최소한 'fully_trusted' (모든 보안 예방 조치를 비활성화해야 함)와 'data' (사용자를 당황하게 할 수 있는 기능을 피해야 함) 문자열을 받는 filter 인자를 사용해야 합니다.

함수 기반 필터 API를 표준화하는 것은 이 PEP의 범위를 벗어납니다.

Shutil

shutil.unpack_archive()filter 인자가 추가됩니다. 지정된 경우, 해당 인자는 하위 추출 함수에 전달됩니다. zip 아카이브에 이를 전달하면 현재는 실패합니다(zipfilefilter 인자가 추가될 때까지이며, 실제로 추가되는 경우에 한합니다).

filter가 지정되지 않았거나 None으로 남겨진 경우에는 전달되지 않으므로, tarball을 추출할 때 기본 필터가 사용됩니다(현재는 사용 중단 경고와 함께 'fully_trusted'가 사용되고, Python 3.14부터는 'data'가 사용됩니다).

복잡한 필터

일부 사용자 정의 필터는 예를 들어 추출된 멤버의 수를 세거나 후처리를 수행해야 할 수 있다는 점에 유의하십시오. 이를 위해서는 filter 호출 가능 객체보다 더 복잡한 API가 필요합니다. 그러나 그러한 복잡한 API를 tarfile에 노출할 필요는 없습니다. 예를 들어 가상의 StatefulFilter를 사용한다면 사용자는 다음과 같이 작성하게 됩니다.:

with StatefulFilter() as filter_func:
    my_tar.extract(path, filter=filter_func)

간단한 StatefulFilter 예제가 문서에 추가됩니다.

Note

상태 유지 필터가 필요한 점은 'fully_trusted', 'tar', 'data'에 더해 사용자 정의 필터 이름의 등록을 허용하지 않아야 하는 이유입니다. 이러한 메커니즘을 사용하면 최소한 설정 및 해제에 대한 API를 확정해 두어야 합니다.

하위 호환성

관련 TarFile.extractTarFile.extractall의 기본 동작은 2개 릴리스 동안 DeprecationWarning을 발생시킨 후(Python의 하위 호환성 정책에서 허용하는 가장 짧은 폐기 기간) 변경됩니다.

또한 tarfile.TarInfo 객체의 동일성에 의존하는 코드는 중단될 수 있으므로 TarInfo 동일성과 offset을 참조하십시오.

백포팅 및 순방향 호환성

이 기능은 이전 버전의 Python으로 백포트될 수 있습니다.

CPython에서는 패치 릴리스에 경고를 추가하지 않으므로, 백포트에서는 기본 필터를 'fully_trusted'로 변경해야 합니다.

그 외에는 tarfile에 대한 변경 사항을 모두 백포트해야 하므로, hasattr(tarfile, 'data_filter')가 모든 새로운 기능을 신뢰성 있게 확인하는 방법이 됩니다.

CPython의 일반적인 정책은 보안 백포트에 새로운 API를 추가하지 않는 것임을 유의하십시오. 이 기능은 새로운 API(TarFile.extraction_filterfilter 인자) 없이는 의미가 없으므로 예외를 적용합니다. (자세한 내용은 Discourse comment 23149/16을 참조하십시오.)

다음은 tarfile에 제안된 기능이 있을 수도 있고 없을 수도 있다는 점을 고려한 코드 예제입니다.

이러한 코드 조각을 복사할 때 extraction_filter를 설정하면 이후 작업에 영향을 준다는 점에 유의하십시오.

  • 완전히 신뢰할 수 있는 아카이브:
    my_tarfile.extraction_filter = (lambda member, path: member)
    my_tarfile.extractall()
    
  • 사용 가능한 경우 'data' 필터를 사용하되, 이 기능을 사용할 수 없으면 Python 3.11의 동작('fully_trusted')으로 되돌립니다.:
    my_tarfile.extraction_filter = getattr(tarfile, 'data_filter',
                                           (lambda member, path: member))
    my_tarfile.extractall()
    

    (안전하지 않은 작업이므로 명시적으로 작성해야 하며, 이상적으로는 주석과 함께 작성해야 합니다.)

  • 'data' 필터를 사용하고, 사용할 수 없으면 실패합니다.:
    my_tarfile.extractall(filter=tarfile.data_filter)
    

    또는:

    my_tarfile.extraction_filter = tarfile.data_filter
    my_tarfile.extractall()
    
  • 'data' 필터를 사용하고, 사용할 수 없으면 경고합니다.:
    if hasattr(tarfile, 'data_filter'):
        my_tarfile.extractall(filter='data')
    else:
        # remove this when no longer needed
        warn_the_user('Extracting may be unsafe; consider updating Python')
        my_tarfile.extractall()
    

보안 영향

이 제안은 하위 호환성을 희생하여 보안을 향상합니다. 특히 사용자가 CVE-2007-4559를 피하는 데 도움이 됩니다.

이 내용을 가르치는 방법입니다.

API, 사용 참고 사항 및 추가 검증을 위한 팁이 문서에 추가됩니다. 이는 일반적으로 아카이브에 익숙하지만 UNIX 파일 시스템의 세부 사항이나 관련 보안 문제에는 익숙하지 않은 사용자가 사용할 수 있어야 합니다.

참조 구현입니다.

GitHub에서 pull request #102953를 확인하십시오.

거부된 아이디어입니다.

SafeTarFile입니다.

Lars Gustäbel이 처음 제안한 아이디어는 보안 검사를 구현하는 별도의 클래스를 제공하는 것이었습니다(gh-65308 참조). 이 접근 방식에는 두 가지 주요 문제가 있습니다.

  • 이름이 오해를 불러일으킵니다. 일반적인 아카이브 작업은 정당한 사용 사례에 영향을 주지 않고 모든 종류의 원치 않는 동작으로부터 “안전하게” 만들 수 없습니다.
  • 안전하지 않은 기본값 문제를 해결하지 못합니다.

그러나 SafeTarFile의 기본 아이디어 중 상당수는 이 PEP에서 재사용되었습니다.

tarfile에 absolute_path 옵션을 추가합니다.

gh-73974는 추출 메서드에 absolute_path 옵션을 추가할 것을 요청합니다. 이는 CVE-2007-4559를 공식적으로 해결하기 위한 최소한의 변경입니다. 인지하지 못한 사용자를 보호하기에도, 신중하고 호기심 많은 사용자에게 충분한 능력을 제공하기에도 부족합니다.

'tar' 필터의 다른 이름

'tar' 필터는 UNIX 계열 파일 시스템에 특화된 기능을 노출하므로 'unix'라고 이름을 붙일 수 있습니다. 또는 'unix-like', 'nix', '*nix', 'posix'라고 할 수도 있습니까?

기능 측면에서 tar 형식UNIX 계열 파일 시스템은 본질적으로 동일하므로 tar는 좋은 이름입니다.

향후 가능한 작업입니다.

zipfile 및 shutil.unpack_archive에 필터 추가

일관성을 위해 zipfileshutil.unpack_archive()filter 인자를 지원하도록 할 수 있습니다. 그러나 이를 위해서는 이 PEP의 작성자가 Python 3.12에 대해 약속할 수 없는 연구가 필요합니다.

zipfile용 필터는 보안에 도움이 되지 않을 가능성이 큽니다. Zip은 주로 플랫폼 간 데이터 번들에 사용되며, 이에 따라 ZipFile.extract의 기본값은 이미 'data' 필터가 수행할 작업과 유사합니다. 절대 경로와 .. 경로 구성 요소를 새로 허용할 'fully_trusted' 필터는 통합된 unpack_archive API 외에는 그다지 유용하지 않을 수 있습니다.

필터는 보안 이외의 사용 사례에도 유용해야 하지만, 그러한 경우에는 일반적으로 사용자 정의 필터 함수가 필요하며, 해당 함수에는 TarInfoZipInfo 모두와 작동하는 API가 필요합니다. 이는 이 PEP의 범위를 확실히 벗어납니다.

이 PEP만 구현되고 zipfile에는 아무것도 변경되지 않는다면, unpack_archive 호출자에게 미치는 영향은 tar 파일의 기본값이 'fully_trusted'에서 더 적절한 'data'로 변경된다는 것입니다. 과도기 동안 Python 3.12-3.13은 DeprecationWarning을 발생시킵니다. 번거롭지만 이를 처리하는 방법은 여러 가지가 있습니다. 예를 들어 filter 인자를 조건부로 추가하거나, TarFile.extraction_filter를 전역으로 설정하거나, Python 3.14까지 경고를 무시하거나 억제할 수 있습니다.

또한 많은 unpack_archive 호출이 안전하지 않을 가능성이 높으므로, DeprecationWarning이 영향을 받는 코드를 검토하는 데 도움이 되는 힌트로 판명되는 경우가 많기를 기대할 수 있습니다.

감사합니다.

이 제안은 많은 사람, 특히 Lars Gustäbel, Gregory P. Smith, Larry Hastings, Joachim Wagner, Jan Matejek, Jakub Wilk, Daniel Garcia, Lumír Balhar, Miro Hrončok 및 많은 다른 사람의 선행 작업과 논의를 바탕으로 합니다.

참고 자료