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

Python 개선 제안 한국어 번역

PEP 752 – 패키지 저장소를 위한 암시적 네임스페이스

Author:
Ofek Lev <ofekmeister at gmail.com>, Jarek Potiuk <potiuk at apache.org>
Sponsor:
Barry Warsaw <barry at python.org>
PEP-Delegate:
Dustin Ingram <di at python.org>
Discussions-To:
Discourse thread
Status:
Accepted
Type:
Standards Track
Topic:
Packaging
Created:
13-Aug-2024
Post-History:
18-Aug-2024, 07-Sep-2024
Resolution:
29-Jun-2026

Table of Contents

번역·라이선스 안내

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

초록

이 PEP는 조직이 향후 업로드에 대비해 패키지 이름 접두사를 예약하는 방법을 명시합니다.

“네임스페이스는 정말 끝내주는 아이디어입니다 – 더 많이 사용합시다!” - PEP 20

동기

현재 생태계에는 안전과 브랜딩을 이유로 자신의 네임스페이스를 완전히 통제하기를 원하는, 패키지가 많은 프로젝트가 검증된 소유권 패턴을 드러낼 방법이 없습니다. 몇 가지 예는 다음과 같습니다:

  • Amazon, Google, Microsoft와 같은 주요 클라우드 제공업체는 각 기능에 대응하는 패키지에 공통 접두사를 사용합니다 [1]. 예를 들어, Google 패키지 대부분에는 google-cloud- 접두사가 붙으며, 가상 머신 사용을 위한 google-cloud-compute가 그 예입니다.
  • OpenTelemetry는 관측 가능성을 위한 개방형 표준으로, 핵심 API와 SDK를 위한 공식 패키지와 다양한 소스에서 데이터를 수집하기 위한 contrib 패키지를 제공합니다. 모든 패키지에는 opentelemetry- 접두사가 붙으며, 하위 접두사는 opentelemetry-<component>-<name>- 형식입니다. contrib 패키지는 중앙 저장소에서 관리되며, 게시 권한은 오직 이 저장소에만 있습니다.
  • Apache Airflow는 워크플로를 프로그래밍 방식으로 작성하고, 일정을 잡고, 모니터링하는 플랫폼입니다. Apache Airflow에는 프로바이더가 있으며, 각 프로바이더 패키지에는 apache-airflow-providers- 접두사가 붙습니다.
  • Typeshed는 다양한 패키지의 타입 스텁을 유지 관리하는 커뮤니티 프로젝트입니다. 이 프로젝트가 유지 관리하는 스텁 패키지는 대상 패키지의 이름을 그대로 따르며 types- 접두사가 붙습니다. 예를 들어, requests 패키지에는 사용자가 의존성으로 지정하는 types-requests라는 스텁이 있습니다. 비공식 스텁은 types- 접두사를 사용하지 않아야 하며, 대신 -stubs 접미사를 사용해야 합니다.

이러한 프로젝트는 이름 선점(name-squatting) 공격에 특히 취약하며, 이는 결국 의존성 혼동으로 이어질 수 있습니다.

예를 들어, 모니터링 기능이 유용할 새로운 제품이 출시되었다고 가정해 봅시다. Datadog이 결국 이 제품을 공식 통합으로 지원하리라고 예상하는 것은 합리적입니다. 로드맵 우선순위 결정과 구현에 필요한 시간 때문에 이러한 통합을 제공하기까지는 상당한 시간이 걸립니다. 잠재적인 모든 패키지의 이름을 예약하는 것은 불가능하므로, 그 사이에 공격자가 정상적으로 보이지만 런타임에 악성 코드(비밀 정보 유출 등)를 실행하는 패키지를 만들 수 있습니다. 사용자가 이러한 패키지를 설치할 가능성이 높아질 뿐만 아니라, 그렇게 되면 프로젝트 전체에 대한 인식도 훼손됩니다. Apache Airflow와 같은 커뮤니티 프로젝트도 이를 겪은 적이 있습니다.

PEP 708이 이 공격 경로에 대응하려 하지만, 이는 의존성 해석 중에 여러 저장소를 고려하는 경우만을 다루며 앞서 언급한 사용 사례에는 아무런 보호도 제공하지 않습니다.

최근 몇 년 동안 타이포스쿼팅은 흔한 공격 경로가 되었습니다 [2]. PyPI가 이에 대응해 사용하는 현재의 보호 방법은 비슷한 문자를 정규화하는 것이지만, 이는 이러한 사용 사례에는 충분하지 않습니다. 네임스페이스를 도입하면 타이포스쿼팅 발생을 크게 줄일 수 있습니다:

  • 오타가 있으려면 접두사 자체에 있어야 합니다. 접두사는 정규화되며, aws-와 같이 짧고 잘 알려진 식별자일 가능성이 높습니다.
  • 색인은 네임스페이스를 신청하고 승인받도록 요구할 수 있으므로, 이러한 경우에 타이포스쿼팅이 일어날 가능성을 줄일 수 있습니다.
  • 공격자는 네임스페이스가 포함된 이름을 선점할 수 없습니다.

근거

다른 패키지 생태계에서는 일반적으로 두 가지 접근 방식 중 하나로 이 문제를 해결해 왔습니다. 하나는 하위 호환성을 최소화하는 방식이고, 다른 하나는 하위 호환성을 최대화하는 방식입니다.

  • NPM에는 범위 지정 패키지라는 개념이 있습니다. 이 개념은 사용할 수 있는 좋은 패키지 이름이 부족하다는 문제(실제 현상이든 그렇게 인식되는 현상이든)에 대응하기 위해 주로 도입되었습니다. 사용자나 조직이 가입하면 그 이름과 일치하는 범위를 받습니다. 예를 들어, Google Cloud Storage를 사용하기 위한 패키지는 @google-cloud/storage이며, 여기서 @google-cloud/가 범위입니다. (조직이 아닌) 일반 사용자 계정은 범위가 지정되지 않은 패키지를 공개용으로 게시할 수 있습니다. 모든 설치 프로그램과 도구가 스코프를 고려하도록 수정되어야 하므로 이 방식은 하위 호환성이 가장 낮습니다.
  • NuGet에는 패키지 ID 접두사 예약이라는 개념이 있습니다. 이 개념은 패키지의 출처를 알고 싶어 하는 사용자의 요구를 충족하기 위해 주로 도입되었습니다. 패키지 이름 접두사는 한 명 이상의 소유자가 사용하도록 예약될 수 있습니다. 예약된 모든 패키지에는 이를 알리는 특별한 표시가 해당 페이지에 나타납니다. 예약 이후에는 사용자가 접두사의 소유자가 아니면 예약된 접두사가 붙은 업로드는 모두 실패합니다. 소유된 접두사가 붙은 기존 패키지는 평소처럼 계속 릴리스할 수 있습니다. PyPI와 같은 색인만 수정하면 되고 설치 프로그램은 바뀔 필요가 없으므로 이 방식은 하위 호환성이 가장 높습니다.

이 PEP는 평면 네임스페이스 전반에 걸쳐 승인된 예약을 두는 NuGet의 접근 방식을 명세합니다. 새로운 패키지 문법이 필요한 해결책은 모두 기존의 평평한 네임스페이스 위에 구축되어야 합니다. 따라서 예약 메커니즘으로 얻는 암묵적 네임스페이스는 그러한 명시적 네임스페이스의 전제 조건이 됩니다.

예약된 네임스페이스와 일치하는 기존 패키지는 그대로 두더라도, 향후의 무단 업로드를 막고 악의적인 경우에 PEP 541 삭제 요청을 전략적으로 적용하면 사용자에게 미치는 위험을 무시할 수 있는 수준으로 줄일 수 있습니다.

용어

이 문서에서 핵심 단어 “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, “OPTIONAL”은 RFC 2119에 설명된 대로 해석해야 합니다.

소유자
소유자는 특정 패키지 이름을 업로드할 수 있는 주체입니다.
부여
부여는 패키지 저장소를 위해 네임스페이스를 예약하는 것입니다.
상위 네임스페이스
네임스페이스의 상위 네임스페이스는 그 네임스페이스에서 하이픈으로 연결된 마지막 구성 요소를 뺀 네임스페이스를 가리킵니다. 예를 들어 foo-bar의 상위 네임스페이스는 foo입니다.
하위 네임스페이스
네임스페이스의 하위 네임스페이스는 그 네임스페이스 끝에 하이픈으로 연결된 구성 요소 하나를 붙인 네임스페이스를 가리킵니다. 예를 들어 foo-bar는 foo의 유효한 하위 네임스페이스입니다.

명세

명명

네임스페이스는 반드시 유효한 프로젝트 이름이어야 하며, 내부적으로 정규화되어야 합니다. 예를 들어 foo.bar는 foo-bar가 됩니다.

의미론

네임스페이스 부여는 다음에 대한 소유권을 부여합니다:

  1. 네임스페이스 자체와 정확히 일치하는 프로젝트.
  2. 네임스페이스 뒤에 하이픈이 붙은 형태로 시작하는 프로젝트. 예를 들어, 네임스페이스 foo는 정규화된 프로젝트 이름 foo-bar와는 일치하지만 프로젝트 이름 foobar와는 일치하지 않습니다.

패키지 이름 일치 여부는 정규화된 네임스페이스를 기준으로 판단합니다.

네임스페이스는 패키지 저장소마다 별도로 존재하며, 저장소 간에 공유되어서는 안 됩니다. 예를 들어, PyPI에 Acme사가 소유한 네임스페이스 acme가 있더라도, PyPI가 아닌 다른 미러 저장소에서 온 acme-로 시작하는 패키지는 같은 수준의 신뢰를 부여받지 못합니다.

부여된 소유권은 서로 겹쳐서는 안 됩니다. 예를 들어, foo-bar에 대한 기존 부여가 있다면 foo에 대한 새 부여는 전자의 소유자만 받을 수 있습니다. 겹침 여부는 정규화된 제안 네임스페이스를 기존의 모든 루트 부여의 정규화된 네임스페이스와 비교하여 판단합니다. 모든 비교에서는 제안 네임스페이스와 기존 네임스페이스의 끝에 하이픈을 덧붙여야 합니다. 기존 네임스페이스 중 하나라도 제안 네임스페이스로 시작하면 겹치는 것으로 판단합니다.

저장소는 네임스페이스에 포함되는 하이픈 개수에 깊이 제한을 두어야 합니다(SHOULD). 예를 들어, 깊이 제한이 1이라면 네임스페이스 foo-bar는 허용되지만 foo-bar-baz는 부여될 수 없습니다.

네임스페이스를 부여하고 관리하는 정책은 색인마다 다르므로 여기에서는 다루지 않습니다. PyPI에 대해 제안된 네임스페이스 정책은 PEP 755에 설명되어 있습니다.

업로드

업로드하는 패키지의 이름이 예약된 네임스페이스와 일치하고, 프로젝트 소유자가 해당 네임스페이스에 대한 활성 부여를 받지 않은 경우, 업로드는 반드시 409 Conflict HTTP 상태 코드와 함께 실패해야 합니다.

저장소는 네임스페이스가 예약되기 전부터 존재하던 프로젝트에 대해서는 이 규칙의 예외를 두어야 합니다(SHOULD).

저장소 메타데이터

JSON API 버전은 1.4에서 1.5로 올라갑니다. 이 PEP를 지원하는 저장소는 반드시 다음 API 변경 사항을 구현해야 합니다. API 사용자가 저장소의 이 PEP 지원 여부를 판단할 수 있도록, 이 PEP를 지원하지 않는 저장소는 이러한 변경 사항을 구현해서는 안 됩니다.

다음 API 변경으로 설치 프로그램은 사용자에게 추가 보안 정책을 제공할 수 있게 됩니다.

프로젝트 상세

프로젝트 상세 응답은 다음과 같이 변경됩니다.

프로젝트가 활성 네임스페이스 부여와 일치하지 않으면 namespaces 키는 반드시 null이어야 합니다. 프로젝트가 네임스페이스 부여와 일치하면, 그 값은 반드시 일치하는 각 네임스페이스를 나타내는 매핑의 배열이어야 합니다. 모든 매핑에는 반드시 다음 키가 있어야 합니다:

  • name: 연관된 정규화된 네임스페이스입니다(예: foo-bar).
  • owned: 불리언 값이며, 프로젝트 소유자가 해당 권한 부여의 현재 소유자 중 하나이면 true입니다. 이 값은 네임스페이스가 예약되기 전에 프로젝트가 이미 존재했고 저장소가 계속 업로드를 허용하는 경우에만 false입니다.

네임스페이스 목록

이 URL의 형식은 /namespaces입니다.

응답은 예약된 각 네임스페이스를 나타내는 매핑의 배열이어야 합니다(MUST). 모든 매핑은 정규화된 네임스페이스(예: foo-bar)를 값으로 하는 name 키를 가져야 합니다(MUST).

네임스페이스 상세

이 URL의 형식은 /namespace/<namespace>이며, 여기서 <namespace>는 정규화된 네임스페이스입니다. 예를 들어, 네임스페이스 foo.bar의 URL은 /namespace/foo-bar입니다.

응답은 다음 키를 가진 매핑이어야 합니다(MUST):

  • name: 네임스페이스의 정규화된 버전입니다(예: foo-bar).
  • parent: 부모 네임스페이스가 존재하는 경우 그 부모 네임스페이스입니다. 예를 들어, 네임스페이스가 foo-bar이고 foo에 대한 활성 권한 부여가 있다면, 이 값은 "foo"입니다. 부모가 없다면 이 키의 값은 null입니다.
  • children: 직계 자식 네임스페이스의 배열입니다. 예를 들어, 네임스페이스가 foo이고 foo-bar와 foo-bar-baz에 대한 활성 권한 부여가 있다면, 이 값은 ["foo-bar"]입니다.

매핑은 네임스페이스의 현재 소유자를 가리키는 owner 키를 가질 수 있습니다(MAY).

권한 부여 제거

예약된 네임스페이스에 소유자가 없어지면, 저장소는 API에서 namespaces 키를 null로 설정해야 합니다(MUST).

커뮤니티의 지지

다음 조직의 대표자들이 이 PEP에 대한 지지를 표명했습니다(논의 링크 포함):

하위 호환성

프로젝트들이 기존의 이름 지정 의미를 계속 사용하므로 본질적인 우려 사항은 없습니다. 사용자 입장에서는 네임스페이스가 있는 프로젝트와 없는 프로젝트를 구별할 수 없습니다. 설치 프로그램은 수정할 필요가 없습니다.

또한 많은 프로젝트가 이미 typeshed가 그랬듯이 접두사를 사용하여 공통된 목적을 드러내는 방식을 택했습니다.

보안 영향

설치 프로그램은 특정 네임스페이스 집합에 일치하고 소유자가 해당 네임스페이스에 대해 유효한 권한을 부여받은 패키지만 허용하는 보안 정책을 활성화하는 기능을 지원할 수 있습니다.

교육 방법

API가 반환하는 새로운 메타데이터를 설명하도록 PyPUG 문서를 갱신할 것입니다.

향후에는 설치 중에 추가적인 보안 보장을 제공하기 위해 네임스페이스 활용을 지원하는 도구에 대해서도 안내할 수 있습니다.

참조 구현

이 PEP의 완전한 참조 구현은 PR #17691에서 확인할 수 있습니다.

기각된 아이디어

사용자가 아닌 주체의 명시적 소유

패키지 저장소는 평평한 네임스페이스를 가지므로, 아무 사용자나 네임스페이스를 예약할 수 있게 하는 것은 받아들일 수 없습니다. contention for a finite resource가 발생하기 때문만이 아니라, 임의의 수의 사용자를 심사하는 일을 관리할 만큼 충분한 사람 연산자를 갖춘 저장소가 없기 때문입니다.

이 PEP의 이전 버전에서는 이러한 현실적인 고려 사항 때문에 조직만 네임스페이스를 예약할 수 있도록 제안했습니다. 그러나 조직이라는 개념이 명세되지 않았고, 예상되는 PyPI 구현을 근거로 그러한 제한을 두는 것은 불필요하므로 이 제안은 기각되었습니다.

조직 스코핑

이 PEP의 주된 동기는 의존성 혼동 공격을 줄이는 것인데, 기존의 평평한 네임스페이스를 허용하면서 NPM 방식의 스코핑을 도입하면 오히려 위험이 커집니다. 문서에서 사용자에게 네임스페이스 foo에 있는 bar를 설치하라고 안내한다면, 사용자는 foo-bar가 아니라 @foo/bar를 설치하도록(또는 그 반대로) 주의해야 합니다. Python 패키징 생태계는 의사소통을 최대한 쉽게 하기 위해 이름에 대한 정규화 규칙을 두고 있으며, 스코핑은 이에 역행하는 일이 됩니다.

Python의 런타임 환경 또한 스코핑에 적합하지 않습니다. 같은 JavaScript 패키지의 여러 버전은 공존할 수 있지만, Python은 하나의 전역 네임스페이스만 허용합니다. 언어 자체에 큰 변화가 없는 한, 이를 바꾸는 것은 거의 불가능합니다.

스코핑은 시간이 지나면서 필연적으로 발생하는 조직 변경의 영향을 특히 크게 받습니다. 조직은 내부 개편, 인수 또는 기타 여러 이유로 이름을 바꿀 수 있습니다. 이런 일이 생길 때마다 그 조직이 소유한 모든 프로젝트의 이름이 사실상 바뀌게 되어, 사용자에게 불필요한 혼란을 자주 일으킵니다.

마지막으로, 모든 패키지 관리자, 보안 스캐너, IDE 등의 업데이트가 필요하므로 커뮤니티가 겪을 혼란이 막대합니다. 스코프를 적용해 릴리스된 새 패키지는 이전 도구와 호환되지 않을 것입니다. 이는 사용자에게 혼란을 주고, 그런 불만을 분류해야 하는 유지 관리자에게는 좌절감을 안겨 줄 것입니다.

아티팩트 수준의 네임스페이스 연결

이 PEP의 이전 버전에서는 릴리스 시점에 메타데이터를 개별 아티팩트와 연결하는 방안을 제안했습니다. 사용자는 네임스페이스 인가 보장이 특정 릴리스가 이루어진 시점이 아니라 현재 부여된 권한을 기준으로 프로젝트 수준에서 적용된다고 기대할 것이므로, 이 방안은 사용자에게 혼란을 일으킬 가능성이 있다는 이유로 거부되었습니다.

HTML Simple API 지원

Simple API의 HTML 버전에서 프로젝트 수준 메타데이터를 노출하는 방법은 두 가지가 있습니다.

첫 번째는 모든 프로젝트를 나열하는 /simple/ 페이지에 data- 속성을 노출하는 것입니다. 이에 대한 선례가 없으며, 설치 프로그램은 일반적으로 이 페이지를 사용하지 않습니다. 또한 이 페이지는 오랜 기간 캐시되는 경우가 많습니다(PyPI의 경우 24시간).

다른 하나는 모든 아티팩트에 data- 속성을 추가하는 것입니다. 이 방법은 거부된 아티팩트 수준 연결 아이디어와 비슷한 혼란을 일으킬 수 있으므로 최선이 아닙니다. 또 다른 고려 사항은, 실제로 많은 사설 색인이 CDN을 앞에 둔 클라우드 스토리지에서 제공하는 정적 페이지로 구현된다는 점입니다. 이 경우 네임스페이스가 변경될 때마다 해당하는 프로젝트의 모든 아티팩트를 일괄 갱신해야 합니다.

전용 패키지 저장소 사용 권장

결정적으로, 이 방법은 프로젝트에 자체 인프라를 유지 관리해야 하는 부담을 지웁니다. 이는 대다수 기업에게 비현실적인 기대이며, 커뮤니티 프로젝트에게는 전혀 실현 불가능한 일입니다.

대부분의 패키지 관리자는 기본적으로 PyPI를 사용하므로 단순히 pip install을 실행하려는 사용자는 이미 악성 패키지에 노출되어 있으며, 따라서 이 방법은 대부분의 경우 도움이 되지 않습니다.

이러한 가상의 미래에서는 모든 프로젝트가 의존성 해석에 자신의 저장소를 추가하는 방법을 문서화해야 하며, 그 방법은 패키지 관리자마다 다릅니다. 특정 의존성을 특정 저장소에서 내려받을 수 있는 패키지 관리자는 거의 없으며, 이러한 패키지 관리자도 일반적인 경우에 사용자에게 장황한 설정을 요구하게 됩니다.

이를 지원하지 않는 패키지 관리자는 대신 순서가 정해진 저장소 목록을 차례로 조회하여 패키지를 찾게 되며, 이는 의존성 혼동으로 이어집니다. 예를 들어, 사용자가 두 사용자 정의 저장소 X와 Y에서 패키지 두 개를 가져오려 한다고 가정해 보겠습니다. 각 저장소에 두 패키지가 모두 있지만 한 패키지는 X에서, 다른 패키지는 Y에서 악성이라면, 사용자는 악성 패키지를 마주치지 않고는 요구 사항을 충족할 수 없습니다.

개방형 네임스페이스

이 PEP의 이전 버전에서는 권한 부여의 소유자가 다른 사람도 해당 네임스페이스로 새 프로젝트를 릴리스할 수 있도록 허용할 수 있게 하는 방안을 제안했습니다. 이 방안은 동기가 충분하지 않고, 저장소가 표준 권한 부여 의미 체계만으로도 이러한 사용 사례를 기술적으로 충족할 수 있다는 이유로 제거되었습니다.

숨겨진 권한 부여

이 PEP의 이전 버전에서는 저장소가 공개적으로 보이지 않는 숨겨진 권한 부여를 만들어 다른 사람이 자신의 네임스페이스를 차지하지 못하게 할 수 있는 방안을 제안했습니다. 이 방안은 동기가 충분하지 않다는 이유로 제거되었습니다.

출처 증명 어설션에만 의존하는 방안

여기서의 아이디어 [3]는 클라이언트가 출처 주장(provenance assertion)을 하여 의존성의 특정 속성을 검증할 수 있는 범용적인 방법을 설계하는 것입니다. 각 주장은 고유한 문법을 가집니다. 몇 가지 예는 다음과 같습니다.

  • 특정 조직 또는 사용자 이름으로 패키지가 업로드되었습니다. 예: pip install "azure-loganalytics from microsoft"
  • 특정 도메인 이름의 소유자가 패키지를 업로드했습니다. 예: pip install "google-cloud-compute from cloud.google.com"
  • 특정 이메일 주소를 가진 사용자가 패키지를 업로드했습니다. 예: pip install "aws-cdk-lib from contact@amazon.com"
  • 네임스페이스와 일치하는 패키지를 권한 있는 주체가 업로드했습니다 (이 PEP)

근본적인 단점은 여러 저장소와 함께 쓸 때 잘 동작하지 않는다는 점입니다. 예를 들어, 사용자가 azure-loganalytics 패키지를 원하며 이 패키지가 microsoft라는 이름의 조직에서 제공된 것인지 확인하고 싶다고 가정해 봅시다. PyPI에서 Microsoft의 조직 이름이 microsoft라면, PyPI를 기본값으로 사용하는 패키지 관리자는 azure-loganalytics from microsoft를 받아들일 수 있습니다. 그러나 의존성 해결에 여러 저장소를 사용한다면 사용자는 정의의 일부로 저장소를 지정해야 하는데, 이는 패키지 소유자 이름 주장을 다루는 별도의 절에서 설명한 이유로 현실적이지 않습니다.

이 방식의 또 다른 일반적인 약점은, 가장 흔한 시나리오인 특별한 문법 없이 단순한 pip install을 수행하려는 사용자가 이미 악성 패키지에 취약하다는 점입니다. 이를 극복하려면 어떤 형태로든 기본 신뢰 메커니즘이 있어야 하며, 이는 어떤 경우든 모든 도구에 특정한 UX나 리졸버 로직을 강요하게 됩니다.

예를 들어, 패키지를 처음 설치할 때 사용자가 출처(provenance) 세부 정보를 보여 주는 확인 프롬프트를 받도록 패키지 관리자를 변경할 수 있습니다. 이는 특히 새로운 사용자에게 매우 혼란스럽고 번거로우며, 기존 사용자에게는 UX를 깨뜨리는 변경이 됩니다. CI에서 실행하거나 requirements 파일로 설치하는 경우처럼 많은 설치 방식이 이 시나리오에서는 동작하지 않으며, 이런 경우 사용자는 수백 개의 프롬프트를 받게 될 수도 있습니다.

사용자에게 덜 방해가 되도록 하는 한 가지 해결책은 신뢰할 수 있는 세부 정보(조직/사용자 이름, 도메인 이름, 이메일 주소 등)의 목록을 수동으로 유지 관리하는 것입니다. 이 목록은 진입점을 제공하는 패키지를 통해 발견할 수 있게 할 수 있으며, 패키지 관리자는 이를 감지하도록 개선될 수 있고 기업 환경에서는 이를 기본으로 설치할 수 있습니다. 이 방식은 자동적인 보장을 제공하지 않는다는 큰 단점이 있으며, 이로 인해 영향을 받을 가능성이 더 높은 일반 사용자에게는 유용성이 제한됩니다.

자동 보호를 제공하는 데 사용할 수 있는 아이디어가 두 가지 있으며, 이는 PEP 740 증명(attestation)이나 메타데이터를 호스팅하는 서드파티 API를 활용하는 새로운 메커니즘에 기반할 수 있습니다.

첫째, 각 저장소는 적절하다고 판단하는 기준을 사용하여 패키지 소유자를 검증하는 서비스를 제공할 수 있습니다. 검증 후 저장소는 해당 세부 정보를 기본으로 설치되는 전용 패키지에 추가합니다.

이를 위해서는 전담 유지 관리가 필요한데, 이는 대부분의 저장소에게, 현재로서는 PyPI에게조차 현실적이지 않습니다. 도메인 이름 같은 것을 마련할 자원이 없는 커뮤니티 프로젝트를 어떻게 지원할지도 불분명합니다. 결정적으로, 이 해결책은 여러 저장소를 사용하는 경우 사용자에게 추가적인 혼란을 일으키는데, 각 저장소가 자체적인 검증 절차, 증명 기준, 그리고 검증된 세부 정보를 담은 기본 패키지를 가질 수 있기 때문입니다. 모든 패키지 관리자가 각 저장소가 선택한 검증 패키지를 인지하고 의존성 해결 전에 이를 기본으로 설치하도록 커뮤니티의 동의를 얻기는 어려울 것입니다.

디지털 증명이 선택된 메커니즘이 된다면, 사용자 정의 패키지 저장소에서 이를 구현하는 데 상당한 양의 작업이 필요하다는 단점이 있습니다. PyPI의 경우, 선행 작업인 Trusted Publishing과 이어진 PEP 740 구현 자체에 풀타임 엔지니어 한 명의 1년치에 해당하는 작업이 들었으며, 그 인건비는 기업 후원사가 부담했습니다. 더 단순한 메커니즘으로도 재현 가능한 빌드를 구현할 수 있으므로, 다른 조직이 비슷한 작업을 구현할 가능성은 낮습니다. 모든 것이 내부에서 관리되는 경우에는 증명도 그다지 유용하지 않습니다. 커뮤니티 프로젝트는 필요한 인프라를 스스로 유지 관리할 자원이 부족할 가능성이 높으므로 이러한 노력을 기울일 가능성이 낮으며, 게다가 전용 패키지 저장소 장려에는 상당한 단점이 있습니다.

다른 아이디어는 출처 주장을 외부에서 호스팅하고 더 많은 로직을 클라이언트 측으로 넘기는 것입니다. 가능한 구현 방법 중 하나는 /provenance와 같이 지정된 상대 경로에 호스팅할 수 있는 출처 API를 명세하는 것입니다. 그러면 각 저장소의 프로젝트가 특정 도메인을 가리키도록 설정할 수 있으며, 이 정보는 설치 중에 클라이언트로 전달됩니다.

이러한 분산 방식은 저장소의 인프라 부담을 덜어 주지만, 보안 위험이 될 수 있습니다. 외부 출처(provenance) API가 침해되면 악성 패키지가 설치될 수 있습니다. 외부 API가 중단되면 패키지 설치가 실패할 수 있습니다. 또는 패키지 관리자가 경고만 출력할 수도 있는데, 이 경우에는 보안상 이점이 전혀 없습니다.

또한 이 방식은 그러한 API를 유지할 자원이 없는 커뮤니티 프로젝트에 불리합니다. 이러한 프로젝트는 많은 프로젝트가 문서 호스팅에 사용하는 것과 같은 무료 호스팅 서비스를 이용할 수 있습니다. 하지만 엄밀히 말해 인프라를 소유하지는 않으므로, 그 관대한 무료 제공이 제한되면 곤란한 처지에 놓이게 됩니다.

마지막으로, 이 두 이론적 접근 방식은 아직 구체적으로 규정되지 않았지만, 아티팩트 수준의 어서션을 전제로 합니다. 이는 이미 기각된 아이디어입니다.

패키지 소유자 이름 어서션

패키지가 특정 조직이나 사용자 이름에서 왔음을 어서션하는 방식입니다. 평면 네임스페이스를 기본 전제로 한다는 점을 제외하면 조직 스코핑 아이디어와 매우 유사합니다.

이 방식을 도입하려면 지원되는 각 저장소의 JSON API를 수정해야 하며, 추가 메타데이터를 노출하거나 정식 출처 어서션으로 구현할 수 있습니다.

조직 스코핑 아이디어와 마찬가지로 microsoft::azure-loganalytics와 같은 새로운 구문이 필요합니다. 여기서 microsoft는 조직이고 azure-loganalytics는 패키지입니다. 이 방식은 상대적으로 기존의 평면 네임스페이스와 잘 어울리지만, 필요한 변경 사항이 많아 커뮤니티에 혼란을 준다는 치명적인 단점은 그대로 남습니다.

이 방식에만 있는 단점은 이름이 저장소의 구현 세부 사항이라는 점입니다. PyPI에서는 조직 이름과 사용자 이름이 별개이므로 충돌이 발생할 수 있습니다. 저장소가 여러 개인 경우, 사용자는 전용 패키지 저장소 권장 거부된 아이디어의 끝부분에서 설명한 것과 비슷한 의존성 혼동 사례를 겪을 수 있습니다.

이를 완화하기 위해 microsoft@pypi.org::azure-loganalytics처럼 예상 저장소 URL도 포함하도록 문법을 확장하자는 제안이 있었습니다. 이 문법이나 이와 비슷한 문법은 너무 장황해서 사용자에게 혼란을 줄 수 있습니다. 더 나쁜 점은, 전용 인프라를 유지할 수 있는 이들 사이에서 널리 채택되면 좌절감을 줄 수 있다는 것입니다(커뮤니티 프로젝트는 혜택을 받지 못합니다).

확장된 문법은 리졸버의 동작과 설정을 의존성 지정자 안에서 표준화하려는 시도입니다. 이는 도구의 UX를 강제할 뿐만 아니라, 패키지 저장소 개념이 있든 없든 여러 언어 생태계의 패키지 관리자에서 전례를 찾을 수 없습니다. 그러한 생태계에서는 리졸버 구성이 의존성 정의와 분리되어 있습니다.

언어 도구 해석 동작
Rust Cargo 의존성 해석은 Cargo.toml 안에서 [patch] 테이블을 사용해 수정할 수 있습니다.
JS Yarn Yarn에는 프로토콜 개념이 있지만(이는 우리의 직접 참조에서 쓰는 URL 스킴과 유사합니다), 사용자는 package.json 파일의 resolutions 필드를 구성합니다.
JS npm 사용자는 package.json 파일에서 overrides 필드를 구성할 수 있습니다.
Ruby Bundler Gemfile에서는 gem의 명시적 소스를 지정할 수 있습니다.
C# NuGet Directory.Packages.props 파일을 구성하여 패키지 버전을 재정의할 수 있습니다.
PHP Composer composer.json 파일에서는 특정 패키지에 대한 저장소 소스를 지정할 수 있습니다.
Go go go.mod 파일에서는 replace 지시문을 지정할 수 있습니다. 이 지시문은 직접 의존성뿐만 아니라 전이 의존성에도 사용된다는 점에 유의하십시오.

고정 접두사 사용

이 방안은 네임스페이스 예약에 사용할 최상위 고정 접두사를 하나 이상 두자는 것입니다:

  • com-: 기업 조직용으로 예약됩니다.
  • org-: 커뮤니티 조직용으로 예약됩니다.

그러면 각 조직은 자기 조직 유형이 접두사로 붙은 네임스페이스를 신청하게 됩니다.

프로젝트를 시작하는 시점에는 사용자 기반이 네임스페이스를 예약해야 할 만큼 커질지 알 수 없으므로, 이 방식은 끊임없는 혼란을 일으킵니다. 그런 상황이 올 때마다 프로젝트 이름을 바꿔야 합니다. 이는 프로젝트 유지 관리자에게 큰 유지보수 부담을 지우고, 프로젝트의 패키지를 참조하는 새로운 방법을 익혀야 하는 사용자에게 혼란을 줄 것입니다. 이 때문에 프로젝트가 네임스페이스 예약 자체를 포기할 가능성이 높습니다.

이 방식의 또 다른 문제는 프로젝트가 대개 브랜딩을 염두에 두고 있어(예시) 패키지 이름을 바꾸기를 꺼린다는 점입니다.

DNS 사용

이 아이디어는 API에서 프로젝트에 domain-authority라는 새 메타데이터 필드를 추가하자는 것입니다. 저장소는 HTTPS로 도메인을 검증하는 새 엔드포인트를 지원하게 됩니다. 그러면 클라이언트는 특정 도메인을 허용하는 옵션을 지원하게 됩니다.

이 방식은 패키지 출처를 확인하지 않는 대상 사용자층의 문제를 해결하지 못하며, 오히려 업로드의 무결성을 확인하는 쪽에 가깝습니다. 업로드 무결성 확인은 이미 PEP 740에서 더 안전한 방식으로 지원하고 있습니다.

대부분의 프로젝트는 도메인이 없어서 이 방식의 혜택을 받을 수 없으므로, 도메인을 확보할 재정적 여유가 있는 조직만 불공평하게 유리해집니다.

미해결 문제

현재로서는 없습니다.

각주