npm 밖으로 넓어진 악성 패키지 경고
GitHub의 악성 패키지 advisory는 더 이상 npm에만 머물지 않는다. GitHub Blog에 따르면 GitHub Advisory Database는 OpenSSF의 malicious-packages 저장소 데이터를 가져오도록 확장됐고, 이 데이터는 Dependabot 경고의 기반으로도 쓰인다. 적용 대상 생태계는 npm, PyPI, Maven, RubyGems, NuGet, Go, crates.io, PHP Composer까지 여덟 곳이다.
개발자에게 중요한 지점은 “새 보안 기능이 생겼다”가 아니라, 내가 쓰는 저장소의 의존성 점검 범위가 실제로 넓어졌다는 점이다. 이전에는 npm 중심으로 확인하던 악성 패키지 신호가 Python, Java, .NET, Go, Rust, PHP 등 여러 생태계로 확장된다. 개인 프로젝트든 연구 코드든 회사 서비스든, 외부 패키지를 설치하는 순간 자격 증명 탈취나 악성 바이너리 실행 위험은 생길 수 있다.
어떤 데이터가 들어오나
GitHub는 기존에도 외부 advisory 저장소를 가져와 Advisory Database에 반영해 왔다. 예를 들어 RubyGems 쪽은 RubySec, Rust 쪽은 RustSec, Python 쪽은 PyPA 같은 공개 데이터를 가져오는 방식이다. 이번 변화는 악성 패키지 영역에서도 비슷한 구조를 택했다는 데 의미가 있다.
핵심 데이터 원천은 OpenSSF의 malicious-packages 저장소다. 이 저장소는 2023년에 시작됐고, OSV 형식의 악성 패키지 보고서를 다룬다. 보고서에는 타이포스쿼팅, dependency confusion, 계정 탈취, 악성 사전 빌드 바이너리 같은 사례가 포함될 수 있다. GitHub는 여덟 개 생태계마다 별도 탐지 시스템을 새로 만드는 대신, 이 공개 저장소를 읽는 하나의 importer를 만들었다.
Dependabot을 쓰는 저장소에서 볼 변화
Dependabot을 사용하는 저장소라면 악성 의존성 경고의 적용 범위를 다시 확인할 필요가 있다. GitHub 설명에 따르면 악성 패키지 경고는 opt-in 성격이므로, 단순히 GitHub 저장소를 쓰고 있다는 사실만으로 모든 경고가 자동 적용된다고 보면 안 된다. 저장소 보안 설정, Dependabot alerts 사용 여부, 조직 정책, 적용 생태계를 함께 봐야 한다.
- JavaScript/TypeScript 프로젝트: npm에 대한 기존 경고만 보고 있었다면, GitHub가 자체 npm 악성 advisory를 OpenSSF 데이터와 중복 처리하는 방식까지 포함해 파이프라인을 조정했다는 점을 이해해야 한다.
- Python 프로젝트: PyPI가 이번 확장의 핵심 대상 중 하나다. 연구용 노트북, 자동화 스크립트, 서버 코드에서 PyPI 패키지를 자주 설치한다면 우선 확인 대상이다.
- 백엔드 서비스: Maven, NuGet, Go, crates.io, PHP Composer를 쓰는 서비스는 Dependabot alerts가 실제 사용하는 패키지 생태계를 포괄하는지 확인해야 한다.
- 오픈소스 관리자: 외부 기여자가 의존성을 추가하는 저장소에서는 악성 패키지 경고가 코드 리뷰 전에 보조 신호로 작동할 수 있는지 봐야 한다.
GitHub가 파이프라인을 보수적으로 만든 이유
악성 패키지 advisory는 일반 취약점 advisory와 성격이 다르다. 일반 취약점은 영향을 받는 버전 범위, 패키지 매핑, 심각도 판단이 중요하고 사람이 검토할 시간이 필요할 수 있다. 반면 악성 패키지는 “이 패키지가 적대적인가”에 가까운 문제이며, 자격 증명을 훔치는 패키지라면 며칠짜리 검토 대기열 자체가 위험이 될 수 있다.
그래서 GitHub는 악성 패키지 advisory를 자동 게시할 수 있도록 하면서도, 잘못된 upstream 데이터가 들어올 가능성을 전제로 방어 장치를 넣었다고 설명한다. 잘못된 보고서가 정상 패키지를 악성으로 표시하거나, 패키지명이 틀리거나, 손상된 출처에서 대량 보고서가 들어오는 경우를 고려한 것이다.
| 장치 | 의미 | 개발자가 이해할 점 |
|---|---|---|
| 배치 상한 | 한 번의 import 실행에서 생성할 수 있는 advisory 수를 제한한다. | 평소보다 비정상적으로 많은 advisory가 들어오면 일부만 반영하는 대신 실행을 멈추도록 설계됐다. |
| 출처 추적 | 각 advisory가 OpenSSF 저장소의 어떤 upstream commit에서 왔는지 추적한다. | 잘못된 경고가 발생했을 때 원인을 빠르게 확인할 수 있다. |
| 배치 롤백 | 문제가 있는 advisory 묶음을 개별 삭제가 아니라 배치 단위로 되돌릴 수 있다. | 자동 게시의 속도와 사고 대응 가능성을 함께 고려한 구조다. |
중복과 오탐을 어떻게 다루나
OpenSSF 저장소에는 GitHub가 기여한 npm 악성 advisory도 포함될 수 있다. GitHub가 이 데이터를 그대로 다시 가져오면 자기 데이터가 되돌아오는 순환 구조가 생긴다. GitHub는 OSV의 origin metadata를 사용해 ghsa-malware로 표시된 항목을 가져오기 전에 제외한다고 설명한다.
또 다른 문제는 데이터 형식 차이다. upstream 생태계 이름과 GitHub 내부 생태계 이름이 완전히 일치하지 않을 수 있고, OSV 레코드의 영향을 받는 버전 표현이 GitHub의 버전 범위 모델과 다를 수 있다. 어떤 레코드는 사용할 수 있는 버전 정보가 부족하거나 상세 설명이 비어 있을 수도 있다. 따라서 importer는 OSV 스키마 기준으로 필수 필드, 타입, 형식을 검증하고, 실패한 레코드는 조용히 고쳐 넣지 않고 거부 및 기록한다.
내 저장소에서 먼저 확인할 항목
이번 변화는 GitHub를 쓰는 모든 개발자에게 동일한 방식으로 체감되지는 않는다. Dependabot alerts를 켰는지, 저장소가 어떤 패키지 생태계를 쓰는지, 조직 설정이 보안 경고를 어떻게 처리하는지에 따라 실제 경험은 달라진다.
- Dependabot alerts 활성화 여부: 저장소 보안 설정에서 Dependabot alerts가 켜져 있는지 확인한다.
- 패키지 생태계: npm만 쓰는지, PyPI나 Maven, NuGet, Go, crates.io, Composer까지 함께 쓰는지 확인한다.
- 경고 처리 흐름: 악성 패키지 경고가 뜨면 누가 확인하고, 누가 의존성을 제거하거나 롤백할지 정한다.
- 자동 업데이트와의 관계: Dependabot version updates 또는 security updates를 함께 쓰는 경우, 경고 확인과 패치 적용 절차를 분리해 봐야 한다.
- 비공개 저장소 정책: 조직 계정에서는 보안 기능 적용 범위와 권한 설정이 개인 계정과 다를 수 있으므로 관리자 설정을 확인한다.
개인 사용자와 연구자가 볼 기준
학생이나 연구자는 임시 프로젝트, 실험 코드, Colab·로컬 노트북에서 패키지를 빠르게 설치하는 일이 많다. 이 경우 보안 경고를 운영팀 수준으로 관리하기 어렵지만, 최소한 GitHub에 올린 코드의 의존성 파일은 점검 대상으로 삼을 수 있다. Python 프로젝트라면 requirements.txt, pyproject.toml, poetry.lock 같은 파일이 실제로 저장소에 반영되는지 확인해야 한다.
연구 코드에서는 재현성을 위해 오래된 패키지 버전을 고정하는 경우가 많다. 이때 일반 취약점뿐 아니라 악성 패키지 경고가 뜨는지 확인하는 습관이 필요하다. 경고가 뜬 패키지를 무조건 최신 버전으로 올릴 수 없다면, 해당 패키지가 실험 재현에 꼭 필요한지, 대체 패키지가 있는지, 격리된 환경에서만 실행할 수 있는지부터 따져야 한다.
팀 개발자가 정해야 할 처리 규칙
팀에서는 경고가 “보였다”는 사실보다 처리 규칙이 중요하다. 악성 패키지 경고는 일반적인 낮은 심각도 취약점과 같은 대기열에 넣어두기 어렵다. 패키지가 실제로 자격 증명을 훔치거나 설치 시 악성 동작을 할 수 있다면, 먼저 사용 여부를 확인하고 배포물에 포함됐는지 추적해야 한다.
- 경고가 뜬 패키지가 런타임 의존성인지 개발 의존성인지 구분한다.
- 락파일에만 남아 있는지, 실제 빌드와 배포에 포함되는지 확인한다.
- CI에서 설치된 적이 있다면 해당 시점의 토큰, 배포 키, 레지스트리 자격 증명을 점검한다.
- 패키지를 제거하거나 대체한 뒤에도 캐시, 빌드 이미지, 배포 산출물에 남아 있는지 확인한다.
- 오탐 가능성이 있어도 경고를 무시하기 전에 upstream advisory와 저장소 변경 이력을 함께 본다.
이번 변화의 한계
GitHub가 OpenSSF 데이터를 가져온다고 해서 모든 악성 패키지를 즉시 탐지한다는 뜻은 아니다. 이 기능은 OpenSSF malicious-packages 저장소에 들어온 보고서, OSV 형식 검증, GitHub importer 처리, Advisory Database 반영, Dependabot alerts 설정이 맞물려야 작동한다. 따라서 “GitHub가 모든 패키지 설치를 실시간 검사한다”는 식으로 이해하면 과하다.
또한 경고가 뜨지 않는다고 해서 패키지가 안전하다고 단정할 수 없다. 특히 새로 만들어진 패키지, 다운로드 수가 적은 패키지, 이름이 인기 패키지와 비슷한 패키지, 설치 스크립트가 많은 패키지는 여전히 별도 검토가 필요하다. Dependabot 경고는 중요한 보조 신호지만, 패키지 선택과 권한 관리까지 대신해 주지는 않는다.
적용 전에 점검할 질문
| 항목 | 확인할 질문 | 판단 기준 |
|---|---|---|
| 설정 | 내 저장소에서 Dependabot alerts가 켜져 있는가? | 꺼져 있다면 먼저 저장소 보안 설정과 조직 정책을 확인한다. |
| 적용 범위 | 내 프로젝트가 여덟 개 지원 생태계 중 어디에 해당하는가? | npm, PyPI, Maven, RubyGems, NuGet, Go, crates.io, Composer 사용 여부를 의존성 파일 기준으로 본다. |
| 권한 | 패키지 설치 환경에 배포 토큰이나 개인 자격 증명이 노출되는가? | CI, 개발 PC, 노트북 환경에서 설치 스크립트가 접근할 수 있는 값을 줄인다. |
| 대응 | 악성 패키지 경고가 떴을 때 제거, 롤백, 토큰 교체 순서가 정해져 있는가? | 운영 서비스라면 경고 확인자와 조치 책임자를 미리 정한다. |
| 검증 | 경고의 upstream 출처를 확인할 수 있는가? | GitHub advisory와 OpenSSF 기록을 함께 보고 오탐 가능성과 실제 사용 여부를 판단한다. |
작은 프로젝트에서 바로 해볼 점
개인 프로젝트라면 모든 보안 체계를 한 번에 바꾸기보다 저장소 하나를 골라 Dependabot alerts 상태부터 확인하는 것이 현실적이다. 그다음 의존성 파일과 락파일이 저장소에 제대로 포함돼 있는지 확인한다. 의존성 파일이 없거나 패키지 설치가 문서에만 남아 있다면 자동 경고가 기대만큼 작동하지 않을 수 있다.
Python이나 Node.js 프로젝트를 예로 들면, 새 패키지를 추가할 때 패키지명 철자를 확인하고, 공식 문서나 저장소 링크가 자연스러운지 본다. 이름이 비슷한 패키지를 잘못 설치하는 타이포스쿼팅은 악성 패키지의 대표적인 경로 중 하나다. Dependabot 경고가 뒤늦게 알려주는 신호라면, 설치 전 확인은 더 앞단의 방어선이다.
출처와 검증
이 글은 GitHub Blog의 공급망 보안 글을 바탕으로 작성했다. 세부 설정, 지원 범위, Dependabot alerts 적용 방식은 저장소와 조직 설정에 따라 달라질 수 있으므로 실제 적용 전 GitHub의 현재 문서를 함께 확인해야 한다.
원문: https://github.blog/security/supply-chain-security/how-we-took-malware-advisories-beyond-npm/