비공개 PyPI 저장소 옵션과 각 옵션의 한계점

지원 종료(EOL)된 PHP용 php2shell 패치(CVE-2026-109618 및 CVE-2026-109619)가 출시되었습니다. 수요가 많아 평소보다 영업팀의 답변이 늦어지고 있으므로, 패치된 패키지를 가장 빠르게 확보하려면 온라인에서 Runtimes용 ELS를 구매하시기 바랍니다 (새 탭에서 열림). 패치 FAQ 읽어보기

이 블로그 글의 AI 요약으로 바로 가기

  • Perplexity를 사용하여 이 게시물을 요약해 주세요
  • Gemini로 이 게시물을 요약해 주세요

주요 내용

  • 비공개 PyPI 저장소는 패키지의 출처를 알려줄 뿐, 설치해도 안전한지는 알려주지 않습니다.
  • –index-url을 특정 인덱스에 지정하는 대신 –extra-index-url을 추가하면, 비공개 저장소가 해결하기 위해 만들어진 보안 취약점이 다시 발생하게 됩니다.
  • 미러나 캐시는 업스트림에서 게시한 모든 내용을, 악성 버전을 포함해서 그대로 제공합니다.
  • 설치 가능한 패키지를 검증된 소스에 연결하려면 구성 변경이 아닌 별도의 검증 단계가 필요합니다.

사설 PyPI 저장소를 구축하는 일은 꽤 깔끔해 보입니다. devpi나 Artifactory를 가동하고, 온보딩 문서에 내부 URL을 기재한 뒤, 티켓을 닫으면 끝이죠. 이제 개발자들이 더 이상 공개 PyPI에서 직접 소스 코드를 가져오지 않을 것입니다. 감사관도 만족스러운 답변을 받게 되고, 악몽도 끝나는 셈이죠.

다만 비공개 저장소는 공개 PyPI를 단순히 미러링하고 있을 뿐입니다. 원본 질문에는 이제 답변이 달렸고, 캐싱도 작동하며, 정책상의 제한 사항도 있습니다. 하지만 특정 패키지가 신뢰할 수 있는지 여부는 여전히 다루어지지 않았습니다. 단지 논의의 초점이 옮겨졌을 뿐입니다.

아키텍처를 선택하기 전에 이 부분을 꼼꼼히 검토해 볼 필요가 있습니다. 비공개 PyPI 저장소에 대한 네 가지 일반적인 접근 방식은 검증 대상 측면에서 큰 차이를 보이며, 기본적인 미러 및 프록시 모델에서는 검증 단계가 아예 빠져 있습니다.

비공개 PyPI 저장소가 실제로 해결해 주는 문제

네 가지 문제, 그리고 이 문제들은 실제로 존재합니다.

가용성, 따라서 PyPI의 공개 서비스 중단이 발생하더라도 배포된 애플리케이션이 중단되지 않습니다. 대역폭 및 속도: 인터넷에서 매일 같은 휠을 수천 번씩 가져오는 것은 낭비이기 때문입니다. 내부 패키지 배포, 이를 통해 사설 라이브러리가 requirements.txt 파일에 묻혀 있는 Git URL 외에 저장될 수 있는 공간을 확보할 수 있습니다. 그리고 정책 통제 지점, 이것이야말로 보안에 있어 실제로 중요한 요소입니다.

이 방식만으로는 패키지 무결성 문제를 해결할 수 없습니다. 풀스루 캐시는 업스트림에서 데이터를 가져와 결과를 저장한 뒤 이를 제공합니다. 만약 업스트림에서 악성코드를 를 제공했다면, 캐시는 그 악성코드를 그대로 보존한 채 이후에도 모든 사용자에게 탁월한 지연 시간으로 계속 제공하게 됩니다.

비공개 PyPI 저장소는 패키지의 출처만 변경할 뿐, 패키지 자체의 내용은 변경하지 않습니다. 저장소 자체를 보안 통제 수단으로 간주하는 것은 이 분야에서 흔히 발생하는 아키텍처상의 실수입니다.

악성 패키지가 공개 레지스트리에 등록되는 공개 레지스트리에 등록되는 악성 패키지의 비율은 는 해마다 증가하고 있으며, 이러한 증가 추세를 뒤따르는 미러는 설계상 그 모든 것을 그대로 반영하게 됩니다. 미러링 메커니즘에는 의도를 필터링하는 기능이 전혀 없습니다.

모든 것을 망치는 ‘index-url’ 외의 실수

pip 에는 서로 바꿔 쓸 수 있을 것처럼 보이는 두 가지 플래그가 있습니다. 하지만 실제로는 그렇지 않으며, 누군가가 이를 악용하기 전까지는 그 차이를 간과하기 쉽습니다.

# 기본 인덱스를 대체합니다. 해당 저장소만 참조됩니다.
pip install –index-url https://pypi.internal.example.com/simple/ mypackage

# 두 번째 인덱스를 추가합니다. pip는 두 인덱스를 모두 검색하며, 우선순위는 없습니다.
pip install –extra-index-url https://pypi.internal.example.com/simple/ mypackage

–extra-index-url이 나타나는 이유는 대개 –index-url만 사용했을 때 먼저 문제가 발생했기 때문입니다. 패키지를 찾을 수 없거나 빌드가 실패했고, 두 번째 인덱스를 추가하니 오류가 해결되었습니다.

이것이 중요한 이유는 다음과 같습니다. pip는 패키지가 어느 인덱스에서 가져와야 하는지에 대한 개념을 가지고 있지 않습니다. pip의 공식 문서에서도 이 점을 분명히 밝히고 있습니다: 검색 대상 위치 간에 우선순위가 없으므로 모든 위치가 확인되며, 요구 사항에 가장 잘 맞는 것이 선택되는데, 실제로는 호환 가능한 버전 중 가장 높은 버전이 선택됩니다. 만약 공격자가 내부용 `internal-billing-utils` 버전 99.0.0을 공개 PyPI에 게시한다면, pip는 주저 없이 사용자의 내부 버전 2.4.1 대신 이를 선택하게 될 것입니다.

그게 바로 의존성 혼동이며, –extra-index-url 옵션이 그 원인이 됩니다.

문서에 적힌 내용이 아니라, 빌드 에이전트가 실제로 어떻게 구성되어 있는지 확인하세요. 환경 변수인 PIP_INDEX_URL과 PIP_EXTRA_INDEX_URL은 pip.conf 설정을 자동으로 재정의하며, 다른 팀에서 상속받은 기본 이미지는 이러한 설정이 눈치채지 못하게 슬쩍 들어오는 흔한 경로입니다.

python -m pip config debug
pip config get global.index-url

`pip config debug` 명령을 실행하면 각 설정이 어떤 파일이나 변수에서 비롯된 것인지 확인할 수 있으며, 이는 대개 추측하는 것보다 더 빠릅니다. 이 기회에 두 가지 PIP 변수의 환경 설정도 함께 확인해 보세요.

해결 방법은 –index-url 옵션을 독점적으로 사용하고, pip가 인식하는 유일한 인덱스로 비공개 저장소를 지정하여 내부 패키지와 업스트림 패키지 모두를 이 저장소를 통해 제공하도록 하는 것입니다. PEP 708 에서는 인덱스가 이러한 관계를 명확히 표시하고 병합 과정에서 발생하는 추측의 여지를 없앨 수 있는 저장소 메타데이터를 제안했으나, 3년간 잠정 상태로 유지된 끝에 필요한 구현이 이루어지지 않아 2026년에 기각되었습니다. 현재는 단일 인덱스 원칙이 실제로 적용되고 있습니다.

비공개 PyPI 저장소를 운영하는 4가지 방법

각 옵션은 패키지 상자에 적힌 제품명이 아니라, 업스트림 패키지를 어떻게 처리하는지에 따라 구분됩니다.

접근 방식 상류 공정 처리 아티팩트 검증 운영 부담
전신 거울 PyPI의 모든 내용을 로컬에 복사합니다. 아니요 높은 수준, 저장 용량은 지속적으로 증가하고 있다
풀스루 프록시 요청 시 데이터를 가져오고 캐시에 저장합니다 아니요 낮음에서 보통
선별된 허용 목록 승인된 패키지만 해결됩니다 사람이 확인한 내용만 높음, 검토 진행 중
검증된 관리형 레지스트리 (예: SecureChain) 제공하기 전에 걸러내고 다시 조리합니다 예 낮음, 외주

다음과 함께 완전 미러링 bandersnatch 를 이용한 전체 미러링이 전통적인 방법이며, 단연코 가장 많은 저장 공간을 차지합니다. PyPI 통계 페이지에 따르면, 이 수치는 계속 증가하고 있습니다. 저장 공간 확보는 쉬운 부분입니다. 그 방대한 양의 데이터에 걸쳐 동기화를 원활하게 유지하는 것이야말로 실제 비용입니다.

풀스루 프록시가 일반적으로 널리 사용됩니다. devpi, proxpi, Nexus Repository, Artifactory, AWS CodeArtifact, Azure Artifacts, Google Artifact Registry: 제품은 서로 다르지만, 그 기반이 되는 아키텍처는 동일합니다. 프록시 및 캐시 기능은 가용성과 캐싱 문제를 잘 해결해 주지만, 업스트림 아티팩트의 신뢰성을 검증하는 데는 아무런 역할을 하지 않습니다. 애초에 그것이 이 기능의 역할이 아니었기 때문입니다.

선별된 허용 목록은 팀이 완전한 검증 파이프라인을 구축하지 않고도 사내에서 운영할 수 있는 가장 강력한 통제 수단 중 하나입니다. 하지만 검토 대기열이 누군가에게는 가장 싫어하는 업무가 되고, 릴리스를 진행하기 위해 승인이 일괄적으로 처리되기 시작하면서, 이 수단은 눈에 띄지 않게 실패하기 마련입니다.

검증된 관리형 레지스트리는 가용성보다는 검증에 중점을 두고 구축됩니다. Python용 SecureChain은 는 이러한 방식으로 작동합니다. 패키지는 설치 가능해지기 전에 소스 코드부터 재컴파일되고 검증을 거치며, 설치 후 캐시되어 나중에 스캔되는 방식이 아닙니다.

방법을 결정하기 전에, `pip download -r requirements.txt`를 실행하고, 결과로 나온 고유 패키지 수(전달 의존성 포함)를 세어보세요. 선별된 허용 목록의 크기를 다음을 기준으로 측정하십시오. 직접 의존성 수만 고려하여 선별된 허용 목록의 규모를 결정하면 검토 작업량이 심각하게 과소평가될 수 있습니다. 실제 작업량의 대부분은 전이적 의존성 트리에서 발생하기 때문입니다.

미러링은 검증이 아니다

파이썬 패키징 구성에서는 세 가지 뚜렷한 질문에 답할 수 있으며, 제품들 간에 그 경계가 모호하기 때문에 이 질문들을 구분하는 것이 중요합니다.

이 아티팩트가 손상 없이 도착했나요? 해시 값이 그 답을 알려주며, pip는 이를 잘 지원합니다.

이 유물은 주장하는 출처에서 실제로 유래한 것일까? 출판사가 관여하는 경우, 출처와 서명이 그 질문에 부분적으로 답을 해준다.

이 아티팩트가 자신이 생성되었다고 주장하는 소스 코드와 일치하는가? 기본 툴체인에는 이 질문에 대한 답이 거의 없다.

세 번째 질문은 조작된 휠을 걸러내는 질문입니다. A 휠 은 빌드 산물이며, 패키징 시스템 내에는 이 휠이 패키지가 연결된 저장소와 일치해야 한다는 요구 사항이 전혀 없습니다. 관리자 계정이 해킹당하면, 해시 값은 일관되고 설치도 문제없이 이루어지지만, 저장소에 존재하지 않는 코드를 포함하는 휠이 생성될 수 있습니다.

pip-compile –generate-hashes requirements.in
pip install –require-hashes -r requirements.txt

다른 어떤 작업을 하든 상관없이 해시 확인 모드를 켜십시오. 이 모드는 전이적 의존성을 포함해 모든 의존성을 해시로 고정하도록 강제하여, 은밀한 대체를 방지하고 설치 과정을 재현 가능하게 만듭니다. 다만, 고정된 아티팩트가 과거에 정당한 것이었는지에 대해서는 아무런 확인도 하지 않습니다. 이 모드는 처음에 해결된 결과를 그대로 고정시킵니다.

세 번째 질문을 해결한다는 것은 소스 코드와 설치 가능한 아티팩트 사이에 신뢰할 수 있는 연결 고리를 구축하는 것을 의미하며, 실제로는 검증된 소스 코드를 통제된 환경에서 재컴파일한 뒤, 출처가 불분명한 업스트림 바이너리가 아닌 해당 빌드 결과를 설치하는 것을 뜻합니다. 이는 단순한 구성 설정 문제가 아니라 의도적으로 수행되는 공학적 작업입니다.

‘Trusted Publishing’과 ‘durabletask’가 밝혀낸 사실

신뢰할 수 있는 게시 는 수명이 긴 PyPI API 토큰을 OIDC를 통해 특정 CI 워크플로우에 발급되는 수명이 짧은 인증 정보로 대체합니다. 이 기능이 생성하는 토큰은 15분 이내에 만료되므로, 시크릿 저장소에 도난당할 위험에 노출된 채로 오래 남아 있는 인증 정보가 없습니다.

TeamPCP 캠페인이 2026년 5월 19일 PyPI에 . 1.4.1부터 1.4.3까지의 버전은 35분 동안 연달아 배포되었으며, 프로젝트의 GitHub Actions 비밀 정보에 저장되어 있던 도난당한 장기 유효 API 토큰을 사용하여 PyPI에 직접 업로드되었습니다. 일치하는 태그도 없고, 게시 워크플로도 실행되지 않았으며, 릴리스가 어떻게 올라갔는지 설명해 줄 저장소 기록도 전혀 없었습니다. 이것이 바로 Trusted Publishing이 차단하기 위해 존재하는 토큰 도용 경로입니다.

Telnyx 및 LiteLLM 는 이미 약 8주 전에 그 교훈을 뼈저리게 배웠습니다. 두 프로젝트 모두 2026년 3월, 리포지토리에 일치하는 태그가 없는 상태에서 악성 릴리스가 PyPI에 등록된 적이 있었으며, 이후 두 프로젝트 모두 ‘Trusted Publishing’으로 전환했습니다. 이는 PyPI의 자체 사고 보고서에도 기록되어 있습니다. 5월에 TeamPCP가 다시 등장했을 때, durabletask는 여전히 이 방식을 채택하지 않은 상태였습니다. 이 글을 작성하는 시점에서 가장 최근 릴리스인 2026년 7월 30일자 1.9.0 버전을 기준으로 볼 때, PyPI는 여전히 해당 파일들이 ‘Trusted Publishing’ 없이 게시된 것으로 표시하고 있습니다.

그 격차야말로 개별 사건 하나보다 이 이야기에서 더 중요한 부분입니다. 이 문제 해결책은 수년 전부터 존재해 왔습니다. 두 건의 주목할 만한 보안 침해 사건이 발생하면서 두 프로젝트가 이 해결책을 도입하게 되었습니다. 몇 주 후, 동일한 공격 캠페인이 또 다른 도난당한 게시자 인증 정보를 이용해 세 번째 프로젝트를 침해했습니다.

Trusted Publishing은 자격 증명 도용 경로에 대해 실제로 효과적인 방법이지만, 명확히 짚어둘 만한 두 가지 한계가 있습니다. 이 기능은 사용자가 배포하는 패키지를 보호할 뿐, 사용하는 패키지는 보호하지 않으므로, 이 기능을 활성화하더라도 이미 프로젝트 트리에 포함된 종속성에는 아무런 영향도 미치지 않습니다. 또한 이 기능은 사용자보다 상류에 있는 모든 유지보수자가 동일한 선택을 해야만 작동하는데, 이는 결코 당연시할 수 없는 일입니다.

제어 보호합니다 보호하지 않습니다
신뢰할 수 있는 출판사 게시한 패키지 설치하는 모든 것
해시 고정 첫 번째 해결 후 대체에 반대 흔들린 첫 결심에 맞서
비공개 저장소 통제 대상 패키지의 출처, 이용 가능 여부, 정책 유물의 완전성
레지스트리 수준 선별 검사 해결되기 전의 아티팩트 직접 작성한 코드

조직에서 패키지를 배포하고 있다면, 이번 주에 ‘신뢰할 수 있는 배포(Trusted Publishing)’ 기능을 활성화하세요. 간단한 설정 변경만으로도 공격 경로 전체를 차단할 수 있습니다.

클라이언트 측 보안 강화

리포지토리 아키텍처가 주목을 받지만, 실제로 규칙이 적용되는 곳은 클라이언트 구성이며, 이곳은 대개 일관성이 부족합니다.

pip이 실행되는 모든 환경에서 다음 네 가지를 확인해 볼 가치가 있습니다. 첫째, 시스템 수준의 pip.conf에 index-url이 하나만 지정되어 있고, 다른 곳에는 extra-index-url이 없어야 합니다. 둘째, 기본 이미지 및 CI 구성에서 PIP_INDEX_URL과 PIP_EXTRA_INDEX_URL이 설정되어 있지 않아야 합니다. 셋째, 개발자가 로컬에서 해시 확인을 건너뛰더라도 CI에서는 해시 확인 모드가 활성화되어 있어야 합니다. 넷째, 빌드 에이전트의 네트워크 아웃바운드 트래픽을 제한하여, 잘못 구성된 클라이언트가 공개 PyPI에 전혀 접속할 수 없도록 해야 합니다.

바로 그 마지막 요소가 정책을 실제 집행으로 이어지게 합니다. 구성 변경이 발생하고, 기본 이미지가 상속되며, 누군가는 항상 배포를 허용하기 위해 플래그를 전달하기 마련입니다. 빌드 에이전트가 승인된 비공개 저장소에만 접근할 수 있도록 허용하는 아웃바운드 정책은 누구의 자제력에 의존하지 않고도 이러한 모든 상황을 차단합니다. 인덱스와 인덱스가 가리키는 파일 호스트가 별개이므로, 단일 호스트명만 차단하는 것만으로는 충분하지 않습니다.

접근 방식 선택

접근 방식은 가장 빠르게 구축할 수 있는 방법이 아니라, 실제로 해결해야 할 문제에 맞춰야 합니다.

가용성과 캐싱을 목표로 할 때는 풀스루 프록시를 선택하는 것이 올바른 결정입니다. 이는 훌륭한 인프라이지만, 보안 경계로 취급되는 순간 그 유용성을 잃게 됩니다. 선별된 허용 목록은 누군가가 검토 대기열이 형식적인 절차로 전락하지 않도록 관리하는 한, 감사가 지목할 수 있는 정책상의 통제 지점을 제공합니다. 에어갭(air-gapped) 환경을 제외하면, 전체 미러링이 저장 공간 비용을 상쇄할 만큼의 가치를 제공하는 경우는 거의 없습니다. 이 세 가지 방법 중 어느 것도 본질적으로 아티팩트의 무결성을 보장하지 않으며, 선별된 허용 목록은 해당 검토 프로세스가 실제로 확립한 수준만큼만 보증을 제공합니다.

만약 아티팩트의 무결성이 단순한 가용성이나 감사 추적뿐만 아니라 진정으로 고려 대상이라면, 신뢰할 수 있는 소스에서 패키지를 재구축하고 해결되기 전에 이를 검사하는 검증 계층이 필요합니다. 검증된 관리형 레지스트리를 이용하면 이러한 기능을 직접 파이프라인을 구축하고 운영할 필요 없이 제공받을 수 있는데, 사내에서 이를 수행하려면 스프린트 단위가 아닌 여러 분기에 걸친 엔지니어링 투자가 필요합니다.

Python용 SecureChain의 활용 분야

앞서 제기했던 세 가지 질문으로 돌아가 보자: ‘손상되지 않은 상태’인지, ‘주장하는 출처’와 일치하는지, ‘출처와 일치하는지’다. 대부분의 비공개 저장소 설정은 첫 번째 질문에서 멈추곤 한다. SecureChain 는 이 세 가지 질문 모두에 답할 수 있도록 설계되었습니다. 모든 패키지는 강화된 환경에서 소스 코드부터 재컴파일되고, 설치 가능해지기 전에 검증을 거치며, SLSA 레벨 3 출처 정보, SBOM 및 VEX 데이터가 함께 제공됩니다. 또한 유지보수 담당자가 떠난 후에도 패치 작업이 지속됩니다.

이것이 여러분의 의존성 구조와 어떻게 비교되는지 확인하고 싶으시다면, 저희 전문가에게 Python용 SecureChain과 귀하의 스택에 적합한 접근 방식에 대해 문의해 보세요.

자주 묻는 질문

비공개 PyPI 저장소란 무엇인가요?

공개 PyPI로 직접 연결하는 대신, 개발자와 빌드 시스템에 패키지를 제공하는 자체 호스팅 파이썬 패키지 인덱스입니다. 이 인덱스는 업스트림을 미러링하거나, 필요에 따라 프록시 및 캐싱 기능을 제공하거나, 승인된 패키지만 제공하거나, 패키지를 제공하기 전에 사전 심사를 수행할 수 있습니다.

비공개 PyPI 저장소를 사용하면 설치가 더 안전해질까요?

패키지를 검증하는 경우에만 해당됩니다. 미러나 풀스루 캐시는 업스트림에서 공개한 모든 내용을 그대로 제공하며, 여기에는 악성 버전도 포함됩니다. 보안상의 이점은 저장소 자체에서 비롯되는 것이 아니라, 그 위에 추가된 선별 또는 관리 단계에서 비롯됩니다.

index-url과 extra-index-url의 차이점은 무엇인가요?

–index-url 옵션은 기본 인덱스를 대체하므로, 해당 저장소만 참조됩니다. –extra-index-url 옵션은 인덱스를 하나 더 추가하며, pip는 구성된 모든 인덱스를 순위 지정 없이 검색합니다. 이로 인해 악의적인 공개 패키지가 우선적으로 선택되어 의존성 혼동 공격을 유발할 수 있습니다.

pip에서 의존성 충돌을 방지하려면 어떻게 해야 하나요?

–index-url을 독점적으로 사용하고, 내부 및 업스트림 패키지를 해당 단일 인덱스를 통해 제공하며, 모든 곳에서 PIP_EXTRA_INDEX_URL을 해제하고, 빌드 에이전트의 외부 접속을 제한하여 승인된 저장소에만 접근할 수 있도록 하십시오.

PyPI 미러 서버를 완전히 운영해야 할까요?

거의 없습니다. 완전한 미러링을 구현하려면 대략 44TB의 데이터를 저장해야 하며, 이 용량은 계속 증가할 전망인데, 풀스루 프록시에 비해 검증 측면에서 별다른 이점이 없습니다. 완전한 미러링은 에어갭 환경에서 가장 적합합니다.

PyPI의 ‘신뢰할 수 있는 게시(Trusted Publishing)’란 무엇인가요?

OIDC 기반의 게시 메커니즘으로, 수명이 긴 API 토큰에 의존하는 대신 특정 CI 워크플로우 하나에 대해 수명이 짧은 인증 정보를 발급합니다. 이 메커니즘은 사용자가 게시하는 패키지를 보호합니다. 사용자가 설치하는 패키지에 대해서는 아무런 조치도 취하지 않습니다.

 

요약
비공개 PyPI 저장소 옵션과 각 옵션의 한계점
기사 이름
비공개 PyPI 저장소 옵션과 각 옵션의 한계점
설명
비공개 PyPI 저장소는 패키지의 출처를 관리할 뿐, 패키지가 안전한지 여부는 관리하지 않습니다. 미러, 프록시, 선별된 레지스트리를 비교해 보세요.
작성자
게시자 이름
TuxCare
게시자 로고

4,500명 이상의 Linux 및 오픈 소스 전문가와 함께하세요!

한 달에 두 번. 스팸이 없습니다.

목차
필요한 오픈 소스 보안에 대한 답변 얻기

4,500명 이상의 Linux 및 오픈 소스 전문가와 함께하세요!

한 달에 두 번. 스팸이 없습니다.