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

Python 개선 제안 한국어 번역

PEP 480 – PyPI 침해에서 살아남기: 패키지의 종단 간 서명

Author:
Trishank Karthik Kuppusamy <karthik at trishank.com>, Vladimir Diaz <vladimir.diaz at nyu.edu>, Justin Cappos <jcappos at nyu.edu>, Marina Moore <mm9693 at nyu.edu>
BDFL-Delegate:
Donald Stufft <donald at stufft.io>
Discussions-To:
Discourse thread
Status:
Draft
Type:
Standards Track
Topic:
Packaging
Requires:
458
Created:
08-Oct-2014

Table of Contents

번역·라이선스 안내

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

초록

이 문서는 종단 간 서명과 최대 보안 모델을 지원하도록 PEP 458을 확장할 것을 제안합니다. 종단 간 서명을 사용하면 클라이언트가 내려받는 배포물에 PyPI와 개발자가 모두 서명할 수 있습니다. PEP 458이 제안한 최소 보안 모델은 배포물이 온라인 키로 서명되므로 배포물의 지속적 전달을 지원하지만, PyPI가 침해되었을 때는 배포물을 보호하지 못합니다. 최소 보안 모델에서는 PyPI 인프라에 저장된 서명 키를 탈취한 공격자가 악성 배포물에 서명할 수 있습니다. 이 PEP에서 설명하는 최대 보안 모델은 PEP 458의 이점(예: PyPI에 업로드된 배포물을 즉시 사용할 수 있다는 점)을 유지하면서, PyPI가 침해되더라도 최종 사용자가 위조된 소프트웨어를 설치할 위험이 없도록 추가로 보장합니다.

이 PEP를 적용하려면 PyPI 인프라를 일부 변경해야 하며, 종단 간 서명에 참여하려는 개발자에게도 몇 가지 변경을 권장합니다. 이러한 변경 사항에는 개발자 키로의 위임을 포함하도록 PEP 458의 메타데이터 레이아웃을 갱신하는 것, 개발자 키를 PyPI에 등록하는 절차를 추가하는 것, 그리고 종단 간 서명을 활용하는 개발자를 위해 업로드 워크플로를 변경하는 것이 포함됩니다. 이 모든 변경 사항은 이 PEP의 뒷부분에서 자세히 설명합니다. 종단 간 서명을 활용하려는 패키지 관리자는 PEP 458에 설명된 메타데이터를 사용하는 데 필요한 작업 외에 추가 작업을 할 필요가 없습니다.

이 PEP는 PEP 458에 적용된 변경 사항을 다루지만, 최대 보안 모델에 주로 초점을 맞추기 위해 그 정보 제공 요소는 제외합니다. 예를 들어, 이 문서에서는 The Update Framework(업데이트 프레임워크)의 개요나 PEP 458의 기본 메커니즘을 다루지 않습니다. PEP 458에 대한 변경 사항에는 스냅숏 과정 수정, 키 침해 분석, 스냅숏 감사, PyPI가 침해되었을 때 취해야 할 조치가 포함됩니다. PyPI가 권장할 수 있는(MAY RECOMMEND) 서명 및 키 관리 절차를 논의하지만, 이를 엄격하게 정의하지는 않습니다. 키와 메타데이터를 관리하도록 릴리스 절차를 어떻게 구현할지는 서명 도구 구현자에게 맡깁니다. 즉, 이 PEP는 배포물의 종단 간 검증을 지원하기 위해 개발자가 반드시(MUST) 업로드해야 하는 메타데이터에 포함될 암호화 키 유형과 서명 형식을 규정합니다.

PEP 상태

커뮤니티는 2014년부터 2018년까지 이 PEP를 논의했습니다. 이 PEP를 구현하는 데 필요한 작업량이 많았기 때문에, 선행 단계인 PEP 458이 승인될 때까지 논의를 미뤘습니다. 2020년 중반 현재 PEP 458은 승인되어 구현이 진행 중이며, PEP 작성자들은 구현에 필요한 자금을 적절히 확보할 수 있도록 승인을 얻는 것을 목표로 합니다.

근거

PEP 458은 PyPI를 The Update Framework(TUF) 프레임워크 [2]와 어떻게 통합해야 하는지 제안합니다. 이 PEP는 pip 같은 최신 패키지 관리자의 보안을 강화하는 방법과, PyPI가 서버 측에서 TUF 메타데이터를 포함하도록 수정되면 막을 수 있는 공격 유형을 설명합니다. 패키지 관리자는 PyPI에서 제공하는 TUF 메타데이터를 참조하여 배포물을 더 안전하게 내려받을 수 있습니다.

PEP 458은 PyPI 저장소의 메타데이터 구조도 설명하며, 프로젝트의 지속적 전달을 지원하고 개발자가 업로드한 배포물에 온라인 암호화 키로 서명하는 최소 보안 모델을 채택합니다. 최소 보안 모델은 혼합 공격(mix-and-match)과 불필요한 의존성 공격 등 소프트웨어 업데이터에 대한 공격 대부분 [5] [6]을 막아 내지만, 종단 간 서명을 지원하고 PyPI가 침해되었을 때 위조된 배포물을 차단하도록 개선할 여지가 있습니다.

PEP 480은 PEP 458을 바탕으로 개발자 서명 지원을 추가하고, 악성 배포물을 막기 위해 온라인 키에 대한 의존도를 줄입니다. PEP 458과 최소 보안 모델의 주요 강점은 자동화되고 단순화된 릴리스 절차입니다. 개발자는 배포물을 업로드한 뒤 PyPI가 그 배포물에 서명하도록 할 수 있습니다. 릴리스 절차의 상당 부분은 온라인 역할이 자동으로 처리하며, 이 방식을 쓰려면 PyPI 인프라에 암호화 서명 키를 저장해야 합니다. 안타깝게도 온라인에 저장된 암호화 키는 도난에 취약합니다. 이 PEP가 제안하는 최대 보안 모델에서는 개발자가 PyPI 사용자에게 제공하는 배포물에 직접 서명할 수 있으며, PyPI 인프라에 저장된 온라인 키가 침해되더라도 최종 사용자가 악성 배포물을 내려받을 위험에 처하지 않습니다.

위협 모델

위협 모델은 다음을 가정합니다:

  • 오프라인 키는 안전하며, 보안이 확보된 상태로 보관되어 있습니다.
  • 공격자는 온라인에 저장된 PyPI의 신뢰 키 중 적어도 하나를 탈취할 수 있으며, 이를 한 번에 또는 일정 기간에 걸쳐 수행할 수 있습니다.
  • 공격자는 클라이언트 요청에 응답할 수 있습니다.
  • 공격자는 클라이언트가 설치하려 하지 않는 프로젝트의 개발자 키를 개수에 상관없이 장악할 수 있습니다.

공격자가 클라이언트로 하여금 업데이트 중인 소프트웨어의 최신 버전이 아닌 다른 것을 설치하게(또는 설치된 상태로 남겨 두게) 만들 수 있다면 공격에 성공한 것으로 간주합니다. 공격자가 업데이트 설치를 방해하는 경우, 공격자의 목표는 클라이언트가 무언가 잘못되었다는 사실을 알아채지 못하게 하는 것입니다.

정의

이 문서의 키워드 “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, “OPTIONAL”은 RFC 2119에 설명된 대로 해석해야 합니다.

이 PEP는 TUF를 PyPI와 통합하는 데 초점을 맞추지만, 독자는 TUF의 설계 원칙 [2]을 읽어 보시기 바랍니다. 또한 독자가 TUF 명세 [3] 및 이 PEP가 확장하는 PEP 458에 익숙해질 것을 권장(RECOMMENDED)합니다.

이 PEP에서 사용하는 다음 용어는 Python 패키징 용어집 [4]에 정의되어 있습니다: 프로젝트, 릴리스, 배포.

이 PEP에서 사용하는 용어는 다음과 같이 정의합니다:

  • 배포 파일: 릴리스를 배포하는 데 사용하는 Python 패키지, 모듈 및 기타 리소스 파일을 담은, 버전이 지정된 아카이브 파일입니다. 이 PEP에서는 배포 파일, 배포 패키지 [4], 또는 간단히 배포판이나 패키지라는 용어를 같은 뜻으로 사용할 수 있습니다.
  • 단순 색인(simple index): 배포 파일로 연결되는 내부 링크를 담은 HTML 페이지입니다.
  • 대상 파일(target files): 대략적으로 말해, 대상 파일은 PyPI에 있는 파일 중 TUF로 무결성을 보장해야 하는 모든 파일입니다. 일반적으로 여기에는 배포 파일과, 단순 색인 같은 PyPI 메타데이터가 포함됩니다.
  • 역할(roles): TUF에서 역할은 어떤 주체가 수행하도록 허가받은 작업의 집합을 포괄하며, 여기에는 그 주체가 서명할 수 있는 메타데이터와 책임지는 패키지가 포함됩니다. PyPI에는 root 역할이 하나 있습니다. root 역할로부터 직접 또는 간접적으로 책임을 위임받은 역할이 여럿 있습니다. “최상위 역할(top-level role)”이라는 용어는 root 역할과 root 역할이 위임한 모든 역할을 가리킵니다. 각 역할에는 그 역할이 제공하도록 신뢰받는 메타데이터 파일이 하나씩 있습니다.
  • 메타데이터: 메타데이터는 역할, 다른 메타데이터, 대상 파일을 기술하는 파일입니다.
  • 저장소(repository): 저장소는 이름이 붙은 메타데이터와 대상 파일로 구성된 리소스입니다. 클라이언트는 저장소에 저장된 메타데이터와 대상 파일을 요청합니다.
  • 일관된 스냅숏(consistent snapshot): 특정 시점에 존재하던 PyPI의 모든 프로젝트의 완전한 상태를 담아낸 TUF 메타데이터와 대상 파일의 집합입니다.
  • 개발자: 특정 프로젝트의 배포 메타데이터와 파일뿐만 아니라 TUF 메타데이터를 갱신할 수 있는 해당 프로젝트의 소유자 또는 유지 관리자입니다.
  • 온라인 키: PyPI 서버 인프라에 반드시(MUST) 저장되어야 하는 개인 암호 키입니다. 이를 통해 보통 해당 키로 자동 서명을 할 수 있습니다. PyPI 인프라를 탈취한 공격자는 이러한 키를 즉시 읽을 수 있습니다.
  • 오프라인 키: PyPI 서버 인프라와 분리하여 반드시(MUST) 보관해야 하는 암호화 개인 키입니다. 이렇게 하면 이 키로 자동 서명을 할 수 없습니다. PyPI 인프라를 침해한 공격자도 이 키를 곧바로 읽을 수는 없습니다.
  • 임계값 서명 방식: 역할은 메타데이터에 서명하려면 n개의 키 중 최소 t개가 필요하다고(REQUIRED) 지정하여 키 유출에 대한 복원력을 높일 수 있습니다. t-1개의 키가 유출되어도 역할 자체를 장악하기에는 부족합니다. 역할에 (t, n)개의 키가 필요하다고 말하는 것은 임계값 서명 속성을 나타냅니다.

최대 보안 모델

최대 보안 모델에서는 개발자가 자신의 프로젝트에 서명하고 서명된 메타데이터를 PyPI에 업로드할 수 있습니다. 이 PEP에서 제안하는 모델에서는 PyPI 인프라가 침해되더라도, 공격자는 claimed 프로젝트의 개발자 키에 접근하지 못하면 그 프로젝트의 악성 버전을 제공할 수 없습니다. 그림 1은 최소 보안 모델의 메타데이터 구성에서 바뀐 점을 보여 줍니다. 즉, 이제 개발자 역할을 지원하며 새로운 위임 역할 세 가지가 생겼습니다. 바로 claimed, recently-claimed, unclaimed입니다. 최소 보안 모델의 bins 역할은 이름이 unclaimed로 바뀌었으며, claimed에 추가되지 않은 모든 프로젝트를 담을 수 있습니다. unclaimed 역할은 이전과 똑같이 동작합니다(즉, PEP 458에서 설명한 대로 이 역할에 추가된 프로젝트는 PyPI가 온라인 키로 서명합니다). 최대 보안 모델이 최소 모델보다 강력한 것은 개발자가 제공하는 오프라인 키 덕분입니다. 최소 보안 모델은 프로젝트의 지속적 배포를 지원하지만, 모든 프로젝트가 온라인 키로 서명됩니다. 즉, 공격자는 개발자의 키까지 장악하지 않고도 최소 보안 모델에서는 패키지를 변조할 수 있지만, 최대 모델에서는 그럴 수 없습니다.

../_images/pep-0480-1.png

그림 1: 최대 보안 모델의 메타데이터 구성 개요입니다. 최대 보안 모델은 지속적 배포를 지원하며, 키가 유출되어도 이를 견뎌 낼 수 있습니다.

개발자가 서명하여 PyPI에 처음 업로드한 프로젝트는 recently-claimed 역할에 추가됩니다. recently-claimed 역할은 온라인 키를 사용하므로, 처음 업로드된 프로젝트도 클라이언트가 바로 사용할 수 있습니다. 어느 정도 시간이 지나면 PyPI 관리자는 최대 보안을 위해 recently-claimed에 나열된 프로젝트를 주기적으로(예: 매달) claimed 역할로 옮길 수 있습니다(MAY). claimed 역할은 오프라인 키를 사용하므로, PyPI가 침해되더라도 이 역할에 추가된 프로젝트는 쉽게 위조할 수 없습니다.

recently-claimed 역할을 unclaimed 역할과 분리한 것은 보안 때문이 아니라 사용성과 효율성 때문입니다. 새 프로젝트 위임을 unclaimed 메타데이터 앞에 추가한다면, 프로젝트가 키를 얻을 때마다 unclaimed를 다시 내려받아야 합니다. 새 프로젝트를 따로 분리하면 가져오는 데이터의 양이 줄어듭니다. 사용성 측면에서도 관리자가 어떤 프로젝트가 이제 claimed 상태인지 더 쉽게 확인할 수 있습니다. 이 정보는 키를 recently-claimed에서 claimed로 옮길 때 필요하며, 자세한 내용은 “일관된 스냅숏 생성” 절에서 다룹니다.

종단 간 서명

종단 간 서명을 사용하면 클라이언트가 내려받는 메타데이터에 PyPI와 개발자 모두 서명할 수 있습니다. 업로드된 프로젝트를 클라이언트에 제공하는 일은 PyPI를 신뢰하여 맡기며(과정 중 이 부분의 메타데이터에는 PyPI가 서명합니다), 개발자는 자신이 PyPI에 업로드하는 배포판에 서명합니다.

프로젝트에 신뢰를 위임하려면 개발자는 PyPI에 공개 키를 하나 이상 제출해야 합니다. 개발자는 같은 프로젝트에 대해 여러 개의 공개 키를 제출할 수 있습니다(예를 들어, 프로젝트의 유지 관리자마다 키 하나씩). PyPI는 프로젝트의 모든 공개 키를 상위 메타데이터에 추가하고, 그 메타데이터에 서명합니다. 초기 신뢰가 확립되고 나면, 개발자는 PyPI에 업로드하는 배포물에 서명해야 합니다. 이때 공개 키 중 적어도 하나에 대응하는 개인 키를 사용합니다. 개발자가 PyPI에 업로드하는 서명된 TUF 메타데이터에는 배포물의 파일 크기와 해시 같은 정보가 들어 있습니다. 패키지 관리자는 이 정보로 내려받은 배포물을 검증합니다.

종단 간 서명을 도입하면 실제로 두 가지가 달라집니다. 프로젝트에 신뢰를 위임하는 추가 관리 작업이 필요하고, 개발자는 서명된 메타데이터를 배포물과 함께 PyPI에 반드시(MUST) 업로드해야 합니다. 구체적으로 PyPI는 프로젝트를 claimed 메타데이터 파일에 추가하고 그 파일에 서명합니다. 이렇게 주기적으로 오프라인 키로 메타데이터에 서명할 것으로 예상됩니다. 반면 최소 보안 모델에서는 프로젝트가 온라인 키로만 서명됩니다. 종단 간 서명에서는 신뢰를 위임하는 데(즉, 오프라인 키로 메타데이터에 서명하는 데) 수작업이 필요합니다. 하지만 이 비용은 한 번만 들며, 그 뒤로 프로젝트는 PyPI 침해에 대해 더 강력한 보호를 받습니다.

메타데이터 서명, 키 관리, 배포물 서명

이 절에서는 PyPI가 서명 도구 구현자에게 권장할 수 있는(MAY) 도구, 서명 체계, 서명 방법을 다룹니다. 개발자는 이 도구를 사용해 배포물에 서명하고 PyPI에 업로드할 것으로 예상됩니다. 아래 하위 절에서 다루는 권장(RECOMMENDED) 도구와 체계를 요약하면 다음과 같습니다. 개발자는 자동화된 방식으로 암호 키를 생성하고 (Ed25519 서명 체계로) 메타데이터에 서명할 수 있습니다(MAY). 이 메타데이터에는 배포물의 진위를 검증하는 데 필요한 정보가 들어 있습니다. 그런 다음 개발자가 메타데이터를 PyPI에 업로드하면 pip 같은 패키지 관리자(즉, TUF 메타데이터를 지원하는 패키지 관리자)가 이 메타데이터를 내려받을 수 있습니다. PyPI에서 배포물을 내려받는 최종 사용자가 TUF를 지원하는 패키지 관리자를 사용한다면, 이 과정은 모두 투명하게 진행됩니다.

처음 세 하위 절(암호 서명 체계, 암호 키 파일, 키 관리)에서는 개발자 릴리스 과정의 암호 구성 요소를 다룹니다. 즉, PyPI가 지원하는 키 유형, 키를 저장하는 방법, 키를 생성하는 방법을 다룹니다. 그다음 두 하위 절에서는 TUF 메타데이터를 지원하도록 수정해야 하는(SHOULD) PyPI 모듈을 다룹니다. 예를 들어 Twine과 Distutils가 수정해야 하는(SHOULD) 프로젝트입니다. 끝으로 마지막 하위 절에서는 서명 도구에 권장되는(RECOMMENDED) 자동화된 키 관리 및 서명 방안을 살펴봅니다.

TUF는 암호 키 유형, 서명, 서명 방법을 유연하게 고를 수 있도록 설계되었습니다. 이어지는 절에서 다루는 도구, 수정 사항, 방법은 서명 도구 구현자를 위한 권장 사항(RECOMMENDATIONS)입니다.

암호 서명 체계: Ed25519

CPython에 포함된 패키지 관리자(pip)는 CPython이 아닌 인터프리터에서도 반드시(MUST) 동작해야 하며, 컴파일해야 하는 의존성을 가질 수 없습니다(즉, PyPI+TUF 통합은 암호 서명을 검증하려고 C 확장을 컴파일하도록 요구해서는 안 됩니다(MUST NOT)). 서명 검증은 반드시 Python에서 이루어져야 하며(MUST), 순수 Python으로 RSA [8] 서명을 검증하는 것은 속도 때문에 실용적이지 않을 수 있습니다. 따라서 PyPI는 Ed25519 서명 체계를 사용할 수 있습니다(MAY).

Ed25519 [9]는 작은 암호 서명과 키를 사용하는 공개 키 서명 시스템입니다. Ed25519 서명 체계의 순수 파이썬 구현이 나와 있습니다. Ed25519 서명 검증은 Python에서 수행하더라도 빠릅니다.

암호 키 파일

구현에서는 키 파일을 AES-256-CTR-Mode로 암호화하고 PBKDF2-HMAC-SHA256으로 비밀번호를 강화할 수 있습니다(기본값은 10만 회 반복이지만 개발자가 이를 재정의할 수 있습니다). 현재 TUF의 Python 구현은 어떤 암호화 라이브러리든 사용할 수 있고(PyCA cryptography 지원은 향후 추가될 예정입니다), PBKDF2의 기본 반복 횟수를 재정의할 수 있으며, KDF를 원하는 대로 조정할 수 있습니다.

키 관리: miniLock

사용하기 쉬운 키 관리 솔루션이 필요합니다. 한 가지 해결책은 비밀번호에서 개인 키를 파생하여 개발자가 여러 컴퓨터에 걸쳐 암호화 키 파일을 관리하지 않아도 되도록 하는 것입니다. miniLock은 이를 구현하는 방법의 한 예입니다. 개발자는 암호화 키를 보조 비밀번호로 여길 수 있습니다. miniLock은 매우 작은 키만 필요로 하는 Ed25519 같은 서명 방식과도 잘 어울립니다.

서드파티 업로드 도구: Twine

Twine 같은 서드파티 도구는 (TUF 메타데이터를 포함하는 배포물을 지원하고자 한다면) 개발자 프로젝트에 서명하고 이를 PyPI에 업로드하도록 수정될 수 있습니다. Twine은 PyPI와 상호 작용하기 위한 유틸리티로, TLS를 사용하여 배포물을 업로드하며 사용자 이름과 비밀번호에 대한 MITM 공격을 방지합니다.

빌드 백엔드

빌드 백엔드는 메타데이터에 서명하고 서명된 배포물을 PyPI에 업로드하도록 수정될 수 있습니다.

자동화된 서명 솔루션

개발자를 위해 사용하기 쉬운 키 관리 솔루션을 권장합니다. 한 가지 접근 방식은 miniLock과 유사하게 사용자 비밀번호에서 암호화 개인 키를 생성하는 것입니다. 개발자 서명은 선택 사항으로 남겨 둘 수 있지만, 각 배포물에는 서명되지 않았을 수 있는 의존성이 매우 많을 수 있으므로 이 접근 방식은 불충분할 수 있습니다. 이러한 의존성 중 하나라도 서명되지 않았다면, 프로젝트가 자신의 배포물에 서명하여 얻는 모든 이점이 무효화됩니다(즉, 공격자는 최종 사용자를 공격하기 위해 서명되지 않은 의존성 중 하나만 침해하면 됩니다). 개발자에게 배포물에 수동으로 서명하고 키를 관리하도록 요구하면 키 서명은 사용되지 않는 기능이 될 것으로 예상됩니다.

서명 도구에는 개발자에게 투명하고 키 에스크로(암호화된 개인 키를 PyPI와 공유하는 것)가 필요 없는, PyPI가 중개하는 기본 키 관리 및 패키지 서명 솔루션을 권장합니다. 또한 서명 도구는 각 개발자의 여러 기기에 걸쳐 개인 키를 공유하는 일을 피해야 합니다. 이는 키 관리 솔루션이 프로젝트마다 여러 개의 키를 지원해야 한다는 것을 의미합니다.

다음은 새로운 개발자가 PyPI에 배포물을 업로드하기 위해 따를 수 있는 자동화된 서명 솔루션의 개요입니다:

  1. PyPI 프로젝트를 등록합니다.
  2. 보조 비밀번호(PyPI 사용자 계정 비밀번호와는 별개)를 입력합니다.
  3. 선택 사항: 두 번째 기기에서 (비밀번호 확인 후) 개발자의 PyPI 사용자 계정에 새 신원을 추가합니다.
  4. 프로젝트를 업로드합니다.
  5. 선택 사항: 프로젝트와 관련된 다른 유지 관리자는 로그인한 뒤 보조 비밀번호를 입력하여 자신의 신원을 프로젝트에 추가할 수 있습니다.

1단계는 개발자가 PyPI 프로젝트를 등록하기 위해 따르는 일반적인 절차입니다.

2단계에서는 암호화된 (개인) 키 파일을 생성하고, Ed25519 공개 키를 PyPI에 업로드하며, 배포물에 대해 생성된 TUF 메타데이터에 서명합니다.

3단계에서 두 번째 기기의 새 신원을 선택적으로 추가할 수 있습니다. 비밀번호만 입력하면 되며, 이때에도 암호화된 개인 키 파일이 생성되고 Ed25519 공개 키가 PyPI에 업로드됩니다. 개발자가 여러 기기에서 릴리스에 서명할 수 있도록 별도의 신원을 생성할 수 있습니다. 기존에 검증된 신원(공개 키가 프로젝트 메타데이터에 포함되어 있거나 PyPI에 업로드된 신원)이 새 신원에 서명합니다. 기본적으로 프로젝트 메타데이터의 서명 임계값은 “1”이며, 다른 검증된 신원도 새 릴리스를 만들어 이 임계값을 충족할 수 있습니다.

4단계에서는 배포 파일과 TUF 메타데이터를 PyPI에 업로드합니다. “스냅숏 프로세스” 절에서는 개발자가 PyPI에 배포를 업로드할 때 따르는 절차를 자세히 다룹니다.

5단계에서는 다른 유지 관리자가 2단계와 비슷한 방식으로 암호화된 키 파일을 생성할 수 있습니다. 이 키들은 PyPI에 업로드하고 TUF 메타데이터에 추가해야 합니다. 이 키는 이후 프로젝트 릴리스를 업로드하는 데 사용할 수 있습니다.

기본적으로 암호 파일과 서명의 생성은 개발자에게 드러나지 않습니다. 즉, 개발자는 패키지가 자동으로 서명된다는 사실을 알 필요가 없습니다. 그러나 서명 도구는 유연해야 합니다. 개발자가 직접 키를 생성하고 키 관리를 스스로 하기를 원할 수도 있기 때문입니다. 이 경우 개발자는 자신의 공개 키를 PyPI에 업로드하기만 하면 됩니다.

repository 및 developer TUF 도구는 현재 앞서 언급한 권고 사항을 모두 지원합니다. 자동 서명 기능만 예외이며, 이 기능은 Distlib, Twine 및 기타 서드파티 서명 도구에 추가되어야 합니다. 자동 서명 기능은 저장소 도구가 제공하는 함수를 호출하여 메타데이터에 서명하고 암호 키 파일을 생성합니다.

스냅숏 프로세스

스냅숏 프로세스는 상당히 단순하며 자동화되어야 합니다(SHOULD). 스냅숏 프로세스는 root, targets, 그리고 위임된 역할의 최신 작업 집합을 반드시 메모리에 유지해야 합니다(MUST). 스냅숏 프로세스는 대략 1분마다 이 최신 작업 집합에 서명합니다. (프로젝트 업로드가 동시성에 안전한 방식으로 최신 위임 메타데이터를 스냅숏 프로세스에 지속적으로 알린다는 점을 상기하십시오. 실제로 스냅숏 프로세스는 최신 작업 집합의 사본에 서명하며, 그동안 메모리에 있는 최신 작업 집합은 프로젝트 트랜잭션 프로세스가 지속적으로 전달하는 정보로 갱신됩니다.) 스냅숏 프로세스는 이전 단계에서 생성된 메타데이터(root, targets, 위임된 역할)를 보증하는 새로운 timestamp 메타데이터를 반드시 생성하고 서명해야 합니다(MUST). 마지막으로, 스냅숏 프로세스는 최신 스냅숏을 나타내는 새로운 timestamp 및 snapshot 메타데이터를 반드시 클라이언트가 사용할 수 있게 해야 합니다(MUST).

claimed 또는 recently-claimed 프로젝트는 PyPI에 대한 트랜잭션에서 타깃(단순 색인과 배포물)뿐 아니라 TUF 메타데이터도 업로드해야 합니다. 프로젝트는 두 디렉터리, 즉 /metadata/(위임된 타깃 메타데이터 파일을 담음)와 /targets/(위임된 타깃 메타데이터로 서명된 프로젝트 단순 색인과 배포물 등의 타깃을 담음)를 포함하는 ZIP 파일을 업로드하여 이를 수행할 수 있습니다.

프로젝트가 PyPI에 메타데이터나 타깃 파일을 업로드할 때마다, PyPI는 해당 프로젝트의 TUF 메타데이터가 최소한 다음 속성을 만족하는지 확인해야 합니다.

  • 해당 프로젝트가 PyPI에 등록한 개발자 키 가운데 임계값 이상의 키가 그 프로젝트 타깃의 “루트”를 나타내는 위임된 타깃 메타데이터 파일(예: metadata/targets/ project.txt)에 반드시 서명했어야 합니다.
  • 위임된 타깃 메타데이터 파일의 서명은 반드시 유효해야 합니다.
  • 위임된 타깃 메타데이터 파일은 만료되지 않아야 합니다.
  • 위임된 타깃 메타데이터는 반드시 타깃과 일치해야 합니다.
  • 위임자는 다른 위임자에게서 자신에게 위임되지 않은 타깃을 위임해서는 안 됩니다.
  • 피위임자는 위임자에게서 자신에게 위임되지 않은 타깃에 서명해서는 안 됩니다.

PyPI가 프로젝트의 TUF 메타데이터를 확인하기로 한 경우, PyPI는 이 요구 사항을 충족하지 않는 메타데이터나 타깃 파일 집합의 게시를 거부할 수 있습니다.

PyPI는 각 프로젝트가 자신이 담당하는 TUF 메타데이터에만 쓸 수 있도록 보장하여 접근 제어를 반드시 시행해야 합니다. 이를 위해 PyPI는 프로젝트 업로드 프로세스가 올바른 메타데이터에 쓰고, 그 메타데이터 안에서도 올바른 위치에 쓰도록 반드시 보장해야 합니다. 예를 들어, unclaimed 프로젝트의 프로젝트 업로드 프로세스는 해당 프로젝트의 타깃에 대해 올바른 위임 unclaimed 메타데이터 안의 올바른 타깃 경로에 반드시 써야 합니다.

드물게 PyPI가 프로젝트용 TUF 메타데이터 형식을 하위 호환성이 없는 방식으로 확장하려 할 수 있습니다. PyPI는 기존 TUF 메타데이터를 하위 호환성이 없는 새 형식으로 업그레이드하려고 프로젝트를 대신하여 자동으로 다시 작성할 수 없다는 점에 유의하십시오. 그렇게 하면 개발자 키로 서명된 메타데이터의 서명이 무효화되기 때문입니다. 대신 패키지 관리자는 서로 호환되지 않는 여러 버전의 TUF 메타데이터를 인식하고 처리할 수 있도록 작성되어야 합니다. 그래야 claimed 프로젝트와 recently-claimed 프로젝트에 자신의 메타데이터를 더 새롭지만 하위 호환성이 없는 형식으로 이전할 합리적인 시간을 줄 수 있습니다. 이러한 버전 변경을 처리하는 한 가지 메커니즘이 TAP 14에 설명되어 있습니다.

새로운 일관된 스냅숏을 만들 디스크 공간이 결국 부족해지면, PyPI는 “mark-and-sweep”과 같은 알고리즘을 사용하여 충분히 오래된 일관된 스냅숏을 삭제할 수 있습니다. 즉, 더 이상 사용되지 않는 timestamp, snapshot(스냅숏) 같은 오래된 메타데이터만 삭제됩니다. 구체적으로 말하면, PyPI는 최신 일관된 스냅숏을 보존하기 위해 최신 일관된 스냅숏의 객체들을 루트(timestamp)부터 순회하면서 방문한 모든 객체에 표시를 하고, 표시되지 않은 모든 객체를 삭제합니다. 최근 몇 개의 일관된 스냅숏도 비슷한 방식으로 보존할 수 있습니다. 일관된 스냅숏을 삭제하면, 클라이언트는 삭제된 일관된 스냅숏의 타깃에 대한 모든 요청에 HTTP 404 응답만 받게 됩니다. 그러면 클라이언트는 (이전과 마찬가지로) 최신 일관된 스냅숏으로 요청을 다시 시도해야 합니다.

TUF 메타데이터를 지원하는 모든 패키지 관리자는 파일을 요청할 때 파일 이름에 해당 파일의 암호학적 해시를 포함하여 (timestamp 메타데이터를 제외한) 모든 메타데이터와 타깃 파일을 다운로드하도록 반드시 수정되어야 합니다. 다음 하위 절에서 권장하는 파일 이름 규칙을 따르면, filename.ext 파일에 대한 요청은 digest.filename 파일에 대한 동등한 요청으로 변환됩니다.

마지막으로, 서버 장애 후 오류를 더 쉽게 복구할 수 있도록 PyPI는 트랜잭션 로그를 사용하여 프로젝트 트랜잭션 프로세스와 큐를 기록해야 합니다.

일관된 스냅숏 생성

PyPI는 프로젝트에 따라 claimed, recently-claimed, unclaimed 메타데이터 중 하나와 관련 위임 메타데이터를 갱신할 책임이 있습니다. 모든 프로젝트는 자신의 메타데이터와 타깃 집합을 반드시 단일 트랜잭션으로 업로드해야 합니다. 업로드된 파일 집합을 “프로젝트 트랜잭션”이라고 합니다. PyPI가 프로젝트 트랜잭션의 파일을 검증할 수 있는 방법은 뒤의 절에서 다룹니다. 이 절에서는 PyPI가 프로젝트 트랜잭션에 어떻게 대응하는지에 초점을 맞춥니다.

모든 메타데이터 및 타깃 파일은 파일 이름에 자신의 BLAKE2b-256 해시의 16진 다이제스트를 반드시 포함해야 하며, PyPI는 파일이 업로드된 후 이를 파일 이름 앞에 붙일 수 있습니다. 이 PEP에서는 PyPI가 digest.filename 형식의 간단한 규칙을 채택할 것을 권장합니다. 여기서 filename은 해시를 포함하지 않은 원래 파일 이름이고, digest는 해시의 16진 다이제스트입니다.

unclaimed 프로젝트가 새 트랜잭션을 업로드하면, 프로젝트 트랜잭션 프로세스는 모든 새 타깃 파일과 관련 위임 unclaimed 메타데이터를 반드시 추가해야 합니다. 프로젝트 업로드 프로세스는 새 위임 unclaimed 메타데이터를 스냅숏 프로세스에 반드시 알려야 합니다.

recently-claimed 프로젝트가 새 트랜잭션을 업로드하면, 프로젝트 업로드 프로세스는 해당 프로젝트의 모든 새 타깃 파일과 위임 targets 메타데이터를 반드시 추가해야 합니다. 프로젝트가 새로운 것이라면, 프로젝트 업로드 프로세스는 해당 프로젝트의 공개 키(반드시 트랜잭션에 포함되어야 함)를 담은 새 recently-claimed 메타데이터도 반드시 추가해야 합니다. recently-claimed 프로젝트의 임곗값은 업로드 프로세스가 “1”로 설정합니다. 마지막으로, 프로젝트 업로드 프로세스는 새 recently-claimed 메타데이터와 해당 프로젝트의 현재 위임 targets 메타데이터 집합을 스냅숏 프로세스에 반드시 알려야 합니다.

클레임된 프로젝트의 업로드 프로세스는 PyPI 관리자가 주기적으로(2주에서 한 달마다 이루어질 수 있는(MAY) 수동 프로세스) 프로젝트를 recently-claimed 역할에서 claimed 역할로 옮긴다는 점이 약간 다릅니다. (프로젝트를 recently-claimed에서 claimed로 옮기는 작업이 수동 프로세스인 이유는 PyPI 관리자가 오프라인 키로 클레임된 프로젝트의 배포본에 서명해야 하기 때문입니다.) 그런 다음 프로젝트 업로드 프로세스는 이 이전을 반영하도록 새 recently-claimed 및 claimed 메타데이터를 반드시 추가해야 합니다(MUST). recently-claimed 프로젝트의 경우와 마찬가지로, 프로젝트 업로드 프로세스는 클레임된 프로젝트의 모든 새 대상 파일과 위임된 targets 메타데이터를 반드시 항상 추가해야 합니다(MUST). 마지막으로, 프로젝트 업로드 프로세스는 새 recently-claimed 또는 claimed 메타데이터뿐 아니라 해당 프로젝트의 현재 위임된 targets 메타데이터 집합도 일관된 스냅숏 프로세스에 반드시 알려야 합니다(MUST).

프로젝트 업로드 프로세스는 PyPI 관리자가 프로젝트를 recently-claimed 역할에서 claimed 역할로 옮기는 경우를 제외하고는 자동화되어야 합니다(SHOULD). 또한 프로젝트 업로드 프로세스는 반드시 원자적으로 적용되어야 합니다(MUST). 즉, 모든 메타데이터와 대상 파일이 추가되거나 – 아니면 하나도 추가되지 않아야 합니다. 프로젝트 트랜잭션 프로세스와 스냅숏 프로세스는 동시에 작동해야 합니다(SHOULD). 마지막으로, 프로젝트 업로드 프로세스는 최신 claimed, recently-claimed, unclaimed 메타데이터를 메모리에 유지하여 새 일관된 스냅숏에서 이들이 올바르게 갱신되도록 해야 합니다(SHOULD).

다음 규칙을 준수한다면 큐는 나타난 순서대로 동시에 처리될 수 있습니다(MAY):

  1. 어떤 두 프로젝트 업로드 프로세스도 같은 프로젝트에 대해 동시에 작업해서는 안 됩니다.
  2. 어떤 두 프로젝트 업로드 프로세스도 같은 위임된 unclaimed 역할에 속하는 unclaimed 프로젝트들에 대해 동시에 작업해서는 안 됩니다.
  3. 어떤 두 프로젝트 업로드 프로세스도 새 recently-claimed 프로젝트들에 대해 동시에 작업해서는 안 됩니다.
  4. 어떤 두 프로젝트 업로드 프로세스도 새 claimed 프로젝트들에 대해 동시에 작업해서는 안 됩니다.
  5. 한 프로젝트 업로드 프로세스가 새 recently-claimed 프로젝트에 대해 작업하는 동안 다른 프로젝트 업로드 프로세스가 새 claimed 프로젝트에 대해 작업해서는 안 되며, 그 반대의 경우도 마찬가지입니다.

메타데이터를 일관되지 않게 읽거나 쓰는 일이 없도록 이 규칙들을 반드시 준수해야 합니다(MUST).

스냅숏 감사

악의적인 당사자가 PyPI를 침해하면, 그들은 온라인 키 중 어느 것으로든 임의의 파일에 서명할 수 있습니다. 오프라인 키를 가진 역할(즉, root와 targets)은 여전히 보호됩니다. 저장소 침해로부터 안전하게 복구하려면, 파일이 신뢰할 수 있는 버전으로만 복원되도록 스냅숏을 감사해야 합니다.

저장소 침해가 탐지되면, 세 가지 유형의 정보에 대한 무결성을 검증해야 합니다:

  1. 저장소의 온라인 키가 침해된 경우, targets 역할이 새 키에 위임하는 새 메타데이터에 서명하게 함으로써 해당 키를 폐기할 수 있습니다.
  2. 저장소의 역할 메타데이터가 변경된 경우, 이는 온라인 키로 서명된 메타데이터에 영향을 미칩니다. 침해 이후에 생성된 모든 역할 정보는 폐기해야 합니다. 그 결과, 새 프로젝트의 개발자는 자신의 프로젝트를 다시 등록해야 합니다.
  3. 패키지 자체가 변조되었을 수 있는 경우, 침해 이전에 신뢰할 수 있는 메타데이터에 존재하던 패키지는 저장된 해시 정보를 사용하여 검증할 수 있습니다. 또한 claimed 역할의 개발자가 서명한 새 배포본은 안전하게 유지할 수 있습니다. 그러나 recently-claimed 또는 unclaimed 역할의 개발자가 서명한 배포본은 모두 폐기해야 합니다.

침해가 발생했을 때 스냅숏을 안전하게 복원할 수 있도록, PyPI는 일정에 따라 PyPI 스냅숏을 복사하는 소수의 자체 미러를 유지해야 합니다(SHOULD). 미러링 프로토콜은 이 목적으로 즉시 사용할 수 있습니다. 미러는 PyPI 미러링만을 담당하도록 보안을 확보하고 격리해야 합니다. 미러들을 서로 대조하여 우발적이거나 악의적인 장애를 탐지할 수 있습니다.

또 다른 방법은 각 스냅숏의 암호학적 해시를 주기적으로 생성하여 트윗하는 것입니다. 예를 들어, 트윗을 받은 사용자가 실제 메타데이터를 제시하면 저장소 유지 관리자는 메타데이터의 암호학적 해시를 검증할 수 있습니다. 또는 PyPI가 외부에서 제공된 메타데이터에 의존하는 대신 자체 버전의 스냅숏을 주기적으로 보관할 수도 있습니다. 이 경우 PyPI는 저장소에 있는 모든 패키지의 암호학적 해시를 구하여 이 데이터를 오프라인 장치에 저장해야 합니다(SHOULD). 어느 패키지의 해시라도 변경되었다면 공격이 발생했다는 뜻입니다.

서로 다른 버전의 메타데이터를 제공하거나 패키지를 특정 버전에 고정하는 공격은 TUF가 암묵적 키 폐기와 메타데이터 불일치 탐지 같은 기법으로 처리할 수 있습니다 [2].

키 침해 분석

이 PEP에서는 최대 보안 모델, 배포본의 지속적 제공을 지원하기 위해 추가해야 하는 TUF 역할, 각 역할의 메타데이터를 생성하고 서명하는 방법, 그리고 개발자가 서명한 배포본을 지원하는 방법을 다루었습니다. 나머지 절에서는 PyPI가 저장소 메타데이터를 감사해야 하는(SHOULD) 방법과, PyPI 침해를 탐지하고 복구하기 위해 PyPI가 사용할 수 있는 방법을 설명합니다.

표 1은 (PyPI 역할 중 어느 것에든 속하는) 암호화 개인 키가 임계값 개수만큼 침해되었을 때 가능한 몇 가지 공격을 요약합니다. 가장 왼쪽 열에는 침해된 역할(또는 역할의 조합)이 나열되어 있고, 오른쪽 열들은 침해된 역할로 인해 클라이언트가 악의적인 업데이트, 동결 공격, 메타데이터 불일치 공격에 취약해지는지를 보여 줍니다.

역할 침해 악의적인 업데이트 동결 공격 메타데이터 불일치 공격
timestamp 불필요 스냅숏과 타깃 또는 위임된 역할 중 어느 하나가 협력해야 합니다 예. root, targets, bin 메타데이터 중 가장 이른 만료 시간까지로 제한됩니다. 불필요 스냅숏이 협력해야 합니다
스냅숏 아니요. timestamp와, targets 또는 위임된 역할 중 하나가 협력해야 합니다. 아니요. timestamp가 협력해야 합니다. 아니요. timestamp가 협력해야 합니다.
타임스탬프 그리고 스냅숏 아니요. targets 또는 위임된 역할 중 하나가 협력해야 합니다. 예. root, targets, bin 메타데이터 중 가장 이른 만료 시간까지로 제한됩니다. 예. root, targets, bin 메타데이터 중 가장 이른 만료 시간까지로 제한됩니다.
targets OR claimed OR recently-claimed OR unclaimed OR project 불필요 타임스탬프와 스냅숏이 협력해야 합니다 해당 없음 타임스탬프와 스냅숏이 필요합니다 해당 없음 타임스탬프와 스냅숏이 필요합니다
(타임스탬프 그리고 스냅숏) 그리고 프로젝트 예 예. 단, root, targets 또는 bin 메타데이터의 만료 시간 중 가장 이른 시간까지로 제한됩니다. 예. 단, root, targets 또는 bin 메타데이터의 만료 시간 중 가장 이른 시간까지로 제한됩니다.
(타임스탬프 AND 스냅숏) AND (recently-claimed OR unclaimed) 예. 단, claimed가 위임하지 않은 프로젝트에 한합니다. 예. 단, root, targets, claimed, recently-claimed, project 또는 unclaimed 메타데이터의 만료 시간 중 가장 이른 시간까지로 제한됩니다. 예. 단, root, targets, claimed, recently-claimed, project 또는 unclaimed 메타데이터의 만료 시간 중 가장 이른 시간까지로 제한됩니다.
(타임스탬프 AND 스냅숏) AND (targets OR claimed) 예 예. 단, root, targets, claimed, recently-claimed, project 또는 unclaimed 메타데이터의 만료 시간 중 가장 이른 시간까지로 제한됩니다. 예. 단, root, targets, claimed, recently-claimed, project 또는 unclaimed 메타데이터의 만료 시간 중 가장 이른 시간까지로 제한됩니다.
root 예 예 예

표 1: 특정 역할 키 조합을 탈취했을 때 가능한 공격. 2013년 9월에 (당시) 최신 버전의 pip가 이러한 공격에 어떻게 취약한지, 그리고 TUF가 이러한 공격으로부터 사용자를 어떻게 보호할 수 있는지가 제시되었습니다 [7]. 오프라인 키로 서명되는 역할은 굵은 글씨로 표시되어 있습니다.

targets나 (프로젝트 targets 메타데이터를 제외한) 위임된 역할을 탈취하더라도 공격자가 곧바로 악의적인 업데이트를 제공할 수 있는 것은 아니라는 점에 유의하십시오. 공격자는 타임스탬프 역할과 스냅숏 역할(둘 다 온라인 상태이므로 침해될 가능성이 더 높습니다)도 침해해야 합니다. 즉, 어떤 공격이든 수행하려면 중간자(man-in-the-middle)로 행동할 수 있어야 할 뿐만 아니라 timestamp 키도 탈취해야 합니다(또는 root 키를 탈취하여 새 timestamp 키에 서명해야 합니다). 동결 공격(freeze attack) 이외의 공격을 수행하려면 스냅숏 키도 침해해야 합니다. 마지막으로, 이 역할들의 키는 온라인에 있으므로 PyPI 인프라가 탈취되면 recently-claimed 프로젝트에 악의적인 업데이트가 유입될 수 있습니다.

키가 탈취된 경우

키 탈취란 개발자나 PyPI 역할에 속한 키 중 임계값 이상의 키가 PyPI 인프라와 함께 탈취되어 PyPI에서 새 메타데이터에 서명하는 데 사용된 경우를 의미합니다.

프로젝트의 개발자 키 중 임계값 이상이 탈취된 경우, 해당 프로젝트는 반드시 다음 단계를 수행해야 합니다.

  1. 프로젝트 메타데이터와 대상 파일은 프로젝트가 탈취되지 않은 것으로 알려진 마지막 정상 일관 스냅숏으로 반드시 복원되어야 합니다. 이는 개발자가 모든 대상 파일을 새 키로 다시 패키징하고 다시 서명하여 수행할 수 있습니다.
  2. 프로젝트의 메타데이터는 반드시 버전 번호를 증가시키고, 만료 시간을 적절히 연장하고, 서명을 갱신해야 합니다.

반면 PyPI는 반드시 다음 단계를 수행해야 합니다.

  1. 침해된 개발자 키를 recently-claimed 또는 claimed 역할에서 폐기하십시오. 이는 침해된 개발자 키를 새로 발급한 개발자 키로 교체하는 방식으로 이루어집니다.
  2. 타임스탬프가 찍힌 새로운 일관된 스냅숏을 반드시 발행해야 합니다.

임계값 수 이상의 타임스탬프, 스냅숏, recently-claimed, unclaimed 키가 침해된 경우, PyPI는 반드시(MUST) 다음 단계를 수행해야 합니다:

  1. 루트 역할에서 타임스탬프, 스냅숏, targets 역할 키를 폐기하십시오. 이는 침해된 타임스탬프, 스냅숏, targets 키를 새로 발급한 키로 교체하는 방식으로 이루어집니다.
  2. recently-claimed 및 unclaimed 키를 새로 발급한 키로 교체하여 targets 역할에서 폐기하십시오. 새 targets 역할 메타데이터에 서명한 뒤 새 키를 폐기하십시오(앞서 설명했듯이 이렇게 하면 targets 메타데이터의 보안이 강화되기 때문입니다).
  3. recently-claimed 역할의 모든 타깃 또는 위임을 비우고, 관련된 모든 위임 targets 메타데이터를 삭제하십시오. 최근에 등록된 프로젝트는 PyPI에 개발자 키를 다시 등록해야 합니다.
  4. recently-claimed 및 unclaimed 역할의 모든 타깃은 timestamp, snapshot, recently-claimed, unclaimed 키 중 어느 것도 침해되지 않았던 것으로 알려진 마지막 정상 일관된 스냅숏과 비교해야 합니다. 침해된 일관된 스냅숏에서 추가, 갱신 또는 삭제된 타깃 중 마지막 정상 일관된 스냅숏과 일치하지 않는 것은 이전 버전으로 복원해야 합니다. 모든 unclaimed 타깃의 무결성을 확인한 후에는 unclaimed 메타데이터를 반드시 다시 생성해야 합니다.
  5. recently-claimed 및 unclaimed 메타데이터는 반드시 버전 번호를 증가시키고, 만료 시간을 적절히 연장하고, 서명을 갱신해야 합니다.
  6. 타임스탬프가 찍힌 새로운 일관된 스냅숏을 반드시 발행해야 합니다.

이렇게 하면 이들 역할 중 하나만 침해되었을 수 있더라도 모든 역할을 선제적으로 보호할 수 있습니다.

임계값 수 이상의 targets 키나 claimed 키가 침해되더라도, 공격자는 타임스탬프 키와 스냅숏 키 없이는 할 수 있는 일이 거의 없습니다. 이 경우 PyPI는 침해된 targets 또는 claimed 키를 각각 root 역할과 targets 역할에서 새 키로 교체하여 반드시 폐기해야 하며, 그것으로 충분합니다.

임계값 수 이상의 타임스탬프, 스냅숏, claimed 키가 침해된 경우, PyPI는 타임스탬프 키나 스냅숏 키가 침해되었을 때 수행하는 단계에 더해 반드시(MUST) 다음 단계도 수행해야 합니다:

  1. targets 역할에서 claimed 역할 키를 폐기하고 새로 발급한 키로 교체하십시오.
  2. claimed 역할의 모든 프로젝트 타깃은 timestamp, snapshot, claimed 키 중 어느 것도 침해되지 않았던 것으로 알려진 마지막 정상 일관된 스냅숏과 비교해야 합니다. 침해된 일관된 스냅숏에서 추가, 갱신 또는 삭제된 타깃 중 마지막 정상 일관된 스냅숏과 일치하지 않는 것은 이전 버전으로 복원할 수 있습니다. 모든 claimed 프로젝트 타깃의 무결성을 확인한 후에는 claimed 메타데이터를 반드시 다시 생성해야 합니다.
  3. claimed 메타데이터는 반드시 버전 번호를 증가시키고, 만료 시간을 적절히 연장하고, 서명을 갱신해야 합니다.

이 단계를 따르면 이들 역할 중 하나만 침해되었을 수 있더라도 모든 역할을 선제적으로 보호할 수 있습니다.

임계값 이상의 root 키가 침해되었다면, PyPI는 targets 역할이 침해되었을 때 수행하는 단계를 반드시 수행해야 합니다. 또한 모든 root 키를 교체해야 합니다.

또한 PyPI가 보안 공지를 통해 침해 사고를 충분히 문서화할 것을 권장합니다(RECOMMENDED). 이러한 보안 공지는 타임스탬프, 스냅숏, 루트 역할의 키가 더 이상 유효하지 않아 pip-with-TUF 사용자가 프로젝트를 설치하거나 업데이트할 수 없을 때 가장 유용할 것입니다. 그러면 사용자는 PyPI 웹 사이트를 방문하여 더 이상 설치나 업데이트를 할 수 없는 이유를 설명하는 보안 공지를 참고한 뒤, 그에 따라 조치를 취할 수 있습니다. 침해로 인해 폐기된 root 키가 임계값 개수에 이르지 않았다면, 임계값 개수의 기존 root 키로 새 root 메타데이터의 무결성을 보증하는 서명을 하므로 새 root 메타데이터를 안전하게 업데이트할 수 있습니다. TUF 클라이언트는 이전에 알고 있던 임계값 개수의 root 키로 새 root 메타데이터의 무결성을 검증할 수 있습니다. 대부분은 이 경우에 해당할 것입니다. 침해로 인해 임계값 개수의 root 키가 폐기된 최악의 경우에는, 최종 사용자가 out-of-band 메커니즘으로 새 root 메타데이터를 업데이트하는 방법을 선택할 수 있습니다.

부록 A: PyPI 빌드 팜과 종단 간 서명

PyPI 관리자는 중앙 빌드 팜을 지원하려고 합니다. PyPI 빌드 팜은 개발자가 업로드한 각 배포판마다 PyPI 인프라에서, 지원되는 플랫폼용 Wheel을 자동으로 생성합니다. 패키지 관리자는 개발자가 서명한 소스 배포판 대신 이러한 PyPI Wheel(소스 배포판보다 훨씬 빠르게 설치할 수 있습니다)을 내려받아 프로젝트를 설치할 가능성이 높습니다. 최대 보안 모델을 구현하기 전에 종단 간 서명을 갖춘 중앙 빌드 팜을 운영할 때의 영향을 조사해야 합니다(SHOULD).

중앙 빌드 팜과 종단 간 서명의 문제는, PyPI 인프라에서 Wheel 배포판이 생성되고 나면 개발자가 이에 서명할 가능성이 낮다는 점입니다. 하지만 Wheel 빌드가 결정론적인 과정이라면, 개발자가 서명한 소스 배포판으로 wheel을 생성하는 것은 여전히 유익할 수 있습니다. 결정론적 빌드가 불가능하다면, 개발자는 온라인 키로 wheel에 서명하는 PyPI 역할에 이러한 wheel에 대한 신뢰를 위임할 수 있습니다.

참고 문헌

감사의 글

이 자료는 미국 국립과학재단(National Science Foundation)의 지원(과제 번호 CNS-1345049 및 CNS-0959138)을 받아 수행한 연구를 바탕으로 합니다. 이 자료에 담긴 모든 의견, 결과, 결론 또는 권고 사항은 저자의 것이며, 반드시 미국 국립과학재단의 견해를 반영하지는 않습니다.

TUF를 PyPI에 사용하기 쉽고 효율적으로 통합하는 방법을 고민하는 데 도움을 주신 Alyssa Coghlan, Daniel Holth, Donald Stufft, Sumana Harihareswara, 그리고 distutils-sig 커뮤니티 전체에 감사드립니다.

Roger Dingledine, Sebastian Hahn, Nick Mathewson, Martin Peck, Justin Samuel은 Tor 프로젝트의 Thandy를 전신으로 하여 TUF를 설계하는 데 도움을 주셨습니다.

TUF 개발에 힘써 주신 Konstantin Andrianov, Geremy Condra, Zane Fisher, Justin Samuel, Tian Tian, Santiago Torres, John Ward, Yuyu Zheng에게 감사드립니다.