GeekNews에 올라온 PyPI 보안팀의 공식 공급망 공격 사고 보고서는 단순히 “악성 패키지가 발견됐다”는 뉴스가 아닙니다. 이 사건이 중요한 이유는, 오픈소스 생태계의 보안 위혘이 더 이상 이론적인 가능성이 아니라 매일 맞이해야 하는 현실 위협으로 자리 잡았음을 보여주기 때문입니다.
Trivy 의존성 취약점이 발판이 되어 이미 널리 쓰이던 litellm과 telnyx 패키지에 악성 코드가 주입된 이번 공격은, 우리가 일상적으로 사용하는 도구들이 얼마나 취약한지를 아주 생생하게 보여줍니다. 그리고 더 중요한 것은, 이 문제를 해결하기 위한 현실적인 방어 전략이 이미 존재한다는 점입니다.
왜 이 주제가 눈에 띄었나
요즘 개발자는 보안 문제를 생각할 때 크게 두 가지 접근 방식을 가집니다.
하나는 “내 코드가 잘 작동하면 그만이다”라는 실용주의적 접근이고, 다른 하나는 “모든 의존성을 직접 검증해야 한다”는 완벽주의적 접근입니다. 하지만 두 방식 모두 현실에서는 한계가 명확합니다.
- 실용주의자들은 제3자 라이브러리에 대한 책임을 너무 가볍게 여겨
- 완벽주의자들은 의존성 관리 자체가 더 큰 부담이 되어
이번 PyPI 공격은 바로 그 중간 지점에서, 현실적인 보안 절차를 강제하는 사건입니다. 우리가 원하는 것은 완벽한 안전이 아니라, 적절한 수준의 의심과 검증이 내장된 개발 워크플로우입니다.
핵심 내용 요약
이번 악성 패키지 사건은 과거와 근본적으로 다른 위형인 점에서 특별합니다.
1) 타이포스쿼팅 패키지가 아닌 실제 기존 패키지 공격
기존 대부분의 PyPI 악성코드는 새로 만든 패키지를 통해 퍼졌습니다. 예를 들어 litellm을 littellm으로 타이포스쿼팅한 패키지를 만들어 업로드하는 방식이죠.
하지만 이번 공격은 완전히 다른 접근을 선택했습니다. 이미 수백만 번 다운로드되고 널리 신뢰받던 litellm과 telnyx 패키지에 직접 악성코드를 주입한 것입니다. 이 방식은 훨씬 더 위험합니다.
- 새 패키지라면 설치 전 간단히 의심할 수 있지만
- 기존 패키지라면 신뢰가 각인되어 있는 상태에서 공격이 이루어집니다
2) API 토큰 탈취를 통한 연쇄 공격
공격의 흐름이 특히 걱정스러운 부분입니다.
- Trivy 같은 보안 도구에 있는 취약점을 통해 개발자의 API 토큰이 탈취됨
- 탈취한 토큰으로 PyPI나 GitHub에 접근
- 신뢰받는 오픈소스 패키지에 악성 코드를 업로드
- 악성 코드는 설치 즉시 실행되어 민감한 정보를 수집해 외부로 유출
이 순환 구조는 한 번의 성공이 수많은 다른 프로젝트로 확산될 수 있음을 의미합니다. 한 개발자의 실수가 수백 개의 다른 프로젝트를 위험에 빠뜨릴 수 있습니다.
3) 공격 속도와 대응 시간의 싸움
사건 타임라인을 보면 공격자와 보안 팀 사이의 싸움이 얼마나 빠르게 진행되는지 알 수 있습니다.
LiteLLM 악성 버전:
- 업로드 → 첫 신고: 1시간 19분
- 첫 신고 → 격리: 1시간 12분
- 총 노출 시간: 2시간 32분
Telnyx 악성 버전:
- 업로드 → 첫 신고: 1시간 45분
- 첫 신고 → 격리: 1시간 57분
- 총 노출 시간: 3시간 42분
이 시간 동안 수만 명의 개발자들이 악성 코드를 설치했을 가능성이 있습니다. 이게 바로 완벽한 방어가 불가능한 이유입니다. 대응보다 먼저 공격이 빠르기 때문입니다.
개발자에게 중요한 이유
이번 사건이 우리에게 주는 교훈은 단순히 “PyPI에 조심하세요”가 아닙니다. 이건 개발 워크플로우 전체를 바꾸는 신호입니다.
의존성 쿨다운 (Dependency Cooldowns)
가장 직접적으로 적용할 수 있는 방법입니다. 최근 배포된 패키지를 일정 기간 설치하지 않도록 설정해서, 보안 연구자와 PyPI 관리자가 대응할 시간을 벌 수 있습니다.
uv (현재 지원 중):
# ~/.config/uv/uv.toml 또는 pyproject.toml
[tool.uv]
exclude-newer = "P3D" # 3일 이내 배포된 패키지 제외pip v26.1 (4월 중 지원 예정):
# ~/.config/pip/pip.conf
[install]
uploaded-prior-to = P3D이 방법은 완벽하지 않습니다. 보안 패치가 필요한 경우에는 즉시 설치해야 하죠. 그래서 Dependabot이나 Renovate 같은 취약점 스캔 도구와 병행해서 사용해야 합니다.
진짜 의존성 락 파일 사용
pip freeze는 락 파일이 아닙니다. 버전만 기록할 뿐, 보안을 위해서는 체크섬/해시가 포함된 진짜 락 파일이 필요합니다.
권장 도구 조합:
uv lock- 빠르고 정확pip-compile --generate-hashes- 호환성 보장pipenv- 사용자 친화적
이렇게 하면 requests==2.31.0이냐 requests==2.31.0+somehash냐의 차이가 생깁니다. 해시까지 동일해야 설치가 가능하기 때문에, 악성 버전이 배포되어도 해시가 다르면 차단됩니다.
2FA와 Trusted Publishers
PyPI는 2024년부터 패키지 배포에 2FA를 의무화했지만, 이건 최소한의 조치입니다. 더 중요한 건 GitHub Actions 워크플로 보안과 Trusted Publishers 사용입니다.
API 토큰의 문제점:
- 장기 유효해서 탈취 시 즉각 탐지가 어려움
- 한 번 발급받으면 관리가 복잡해짐
- 회고(Retrospective) 분석이 어려움
Trusted Publishers의 장점:
- 단기 토큰을 사용해 탈취되더라도 즉시 사용 불가능
- Digital Attestations로 정상 배포 여부 검증 가능
- 배포 로그 추적이 용이함
오픈소스 관리자에게 중요한 이유
개발자가 아니라 오픈소스 프로젝트를 운영하는 사람이라면, 더 적극적인 조치가 필요합니다.
GitHub Actions 워크플로 보안 강화
가장 위험한 패턴은 pull_request_target입니다. 이 타겟을 사용하면 외부 사람의 PR이 내부 워크플로를 실행시킬 수 있습니다.
안전하지 않은 패턴:
on:
pull_request_target:
types: [opened, synchronize]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}안전한 대안:
- 파라미터·입력값 무해화 (환경변수로 전달)
- 가변 참금 사용 금지 (Git 태그 대신 커밋 SHA 사용)
- Trusted Publishers + GitHub Environments 조합 사용
Zizmor 도구로 워크플로 점검:
npx zizmor .github/workflows/릴리스 프로세스 재설계
기존의 “개발자가 API 토큰을 관리”하는 방식에서, “시스템이 자동으로 검증하는” 방식으로 전환해야 합니다.
좋은 방향:
- 개발자 로컬에서 작업 → GitHub에 푸시
- GitHub Actions에서 자동 테스트 실행
- 테스트 통과 시 Trusted Publishers가 PyPI에 배포
- 배포 로그와 Digital Attestations로 검증
이렇게 하면 개발자가 직접 API 토큰을 다룰 필요가 없어집니다.
지금 바로 할 수 있는 현실적인 조치
완벽한 보안은 불가능하지만, 적절한 수준의 보안은 가능합니다.
1. 개발자용 체크리스트
-
~/.config/uv/uv.toml또는~/.config/pip/pip.conf에 쿨다운 설정 추가 - 프로젝트에서
uv lock또는pip-compile --generate-hashes사용 - PyPI, GitHub, GitLab 모든 계정에 2FA (하드웨어 키 권장)
- 사용 중인 오픈소스 프로젝트의 워크플로를 Zizmor로 점검
2. 오픈소스 관리자용 체크리스트
- GitHub Actions에서
pull_request_target사용 중단 - 릴리스 워크플로를 Trusted Publishers로 전환
- 환경변수로 민감한 정보 전달 (템플릿 인젝션 방지)
-
actions/checkout@v4같은 신뢰할 수 있는 액션 버전 고정
3. 팀 조직용 체크리스트
- Dependabot/Renovate 설정으로 취약점 모니터링
- 의존성 업데이트 시 쿨다운 기간과 보안 스캔 병행
- 외부 라이브러리 사용 시 보안 정문 문서화
- 정기적인 오픈소스 보안 교육 및 인식 제고
마무리
PyPI 공급망 공격 사건은 우리에게 중요한 질문을 던집니다.
개발의 편리함과 보안의 확실성, 어느 쪽을 선택할 것인가?
하지만 이건 잘못된 질문입니다. 진짜 질문은 다음과 같아야 합니다.
어떻게 하면 편리함과 보안을 동시에 추구할 수 있을까?
이번 사건이 보여준 것처럼, 기술적 해결책은 이미 충분히 존재합니다. uv의 쿨다운, pip-compile의 해시 검증, Trusted Publishers의 자동 검증 시스템 등이 바로 그 예입니다.
앞으로 오픈소스 생태계가 더 중요해질수록, 우리는 더 현실적이고 체계적인 보안 절차를 내장해야 합니다. 완벽한 안전은 불가능하지만, 적절한 수준의 의심과 검증이 내장된 개발 워크플로우는 충분히 가능합니다.
그래야 우리는 악성 패키지의 공격을 완전히 막지는 못해도, 그 피해를 현실적으로 통제할 수 있을 것입니다.