npm 패키지를 쓰는 개발자라면 Trusted Publishing이 더 안전한 게시 방식이라는 말을 한 번쯤 들어봤을 겁니다. 장기 토큰 대신 CI 워크플로가 짧은 자격 증명을 받아 패키지를 올리는 구조라, 토큰 유출 위험을 줄여준다고 소개됐습니다. 그런데 2026년 10월 13일 Bloom Security 연구 결과가 공개되면서, 이 기능 자체가 특정 조건에서 패키지 탈취 통로가 됐다는 사실이 알려졌습니다.
– npm Trusted Publishing이 GitHub 저장소 이름(slug)만 보고 권한을 부여한 탓에, 이름이 바뀌거나 삭제된 저장소를 공격자가 다시 만들면 게시 권한을 얻을 수 있었습니다.
– Bloom Security는 이렇게 노출된 패키지가 1,500개 이상, 총 다운로드 1,000만 회 이상이라고 집계했습니다.
– GitHub는 6월에 보고를 받은 뒤 10월 2일 미검증 설정에 48시간 만료를 적용하는 수정을 배포했습니다.
– 유지보수자는
npm trust list로 현재 바인딩을 확인하고, 이름 변경·이전된 저장소라면 재설정해야 합니다.– 일반 사용자도 provenance가 가리키는 저장소가 원래 프로젝트와 맞는지 확인하는 습관이 필요합니다.
Trusted Publishing이 해결하려 했던 문제
npm Trusted Publishing은 2025년 7월 정식 공개된 기능입니다. 기존에는 유지보수자가 장기 유효 npm 토큰을 GitHub Actions 시크릿에 넣고 쓰는 경우가 많았습니다. 이 토큰이 유출되면 공격자가 아무 때나 악성 버전을 올릴 수 있었습니다. Shai-Hulud 같은 공급망 웜이 바로 이 토큰을 훔쳐 확산한 사례가 있었습니다.
Trusted Publishing은 이 문제를 OIDC로 바꿨습니다. 유지보수자가 npm 설정에서 “이 GitHub 저장소의 이 워크플로 파일만 게시할 수 있다”고 등록하면, 해당 워크플로가 실행될 때 GitHub가 짧은 수명의 서명된 토큰을 발급하고, npm이 이를 게시 자격 증명으로 교환합니다. 토큰이 몇 분 안에 만료되므로 훔쳐도 쓸모가 없어집니다. 동시에 패키지에는 provenance 증명이 자동으로 붙어, 어느 저장소에서 빌드됐는지 확인할 수 있게 됩니다.
방향 자체는 맞았습니다. 장기 토큰보다 훨씬 낫고, 많은 프로젝트가 이 방식으로 전환했습니다. 문제는 구현 세부사항이었습니다.
취약점이 실제로 어떻게 작동했나
npm은 Trusted Publishing 설정을 저장할 때 GitHub 저장소의 이름, 즉 owner/repo 형태의 slug만 기록했습니다. GitHub는 저장소와 소유자에게 영구적인 숫자 ID를 부여하고, OIDC 토큰에도 이 ID를 넣습니다. 그런데 npm은 이 ID를 검증하지 않았습니다.
그래서 다음과 같은 일이 가능했습니다.
- 유지보수자가 특정 저장소 이름으로 Trusted Publishing을 설정합니다.
- 나중에 그 저장소의 이름을 바꾸거나, 다른 계정으로 이전하거나, 삭제하거나, 소유자 계정 자체가 이름 변경·삭제됩니다.
- 옛 slug가 다시 사용 가능해집니다.
- 공격자가 같은 이름으로 새 저장소를 만들고, 설정에 등록된 것과 동일한 이름의 워크플로 파일을 넣습니다.
- 워크플로를 실행하면 npm이 토큰을 받아 원래 패키지에 새 버전을 게시할 수 있습니다.
이렇게 올라간 버전은 유효한 provenance 증명을 함께 갖습니다. 증명은 “이 저장소에서 빌드됐다”는 사실만 확인하지, 그 저장소가 원래 소유자 것인지, 코드가 정직한지는 보지 않습니다. Bloom Security는 연구자들이 직접 통제하는 패키지와 저장소로 이 공격을 처음부터 끝까지 재현했습니다.

GitHub는 저장소가 삭제되거나 계정이 이름 변경되기 전 일주일 동안 100회 이상 클론된 저장소의 이름은 일정 기간 재사용할 수 없게 하는 보호 장치가 있습니다. Bloom은 이 점을 고려해 “재사용 가능한 slug”만 취약한 것으로 집계했다고 밝혔습니다. 그럼에도 1,500개 이상이 해당됐습니다.
얼마나 많은 패키지가 노출됐나
Bloom Security에 따르면 이렇게 노출된 패키지는 1,500개 이상입니다. 총 다운로드 수는 1,000만 회를 넘고, 월간 다운로드는 100만 회 이상입니다. 평균적으로 패키지당 월 수백 회 정도지만, 하나가 탈취돼 악성 코드가 들어가면 그 패키지를 설치하는 모든 환경이 위험해집니다. npm install 시 자동으로 실행되는 postinstall 스크립트가 특히 위험합니다.
Bloom은 영향을 받은 패키지 목록을 공개하지 않았습니다. 그래서 일반 사용자는 “내 패키지가 목록에 있나”를 직접 대조할 수 없고, 유지보수자 쪽에서 설정을 확인하는 수밖에 없습니다. 연구진은 실제 악용 사례가 있었는지는 알 수 없다고 했습니다.
GitHub는 어떻게 고쳤나
Bloom은 2026년 6월 6일 GitHub 버그 바운티 프로그램(HackerOne)을 통해 문제를 보고했습니다. GitHub는 6월 9일 이를 검증했습니다. 이후 몇 차례 후속 연락이 있었으나, Bloom 측은 7월 8일 이후 응답이 없었다고 밝혔습니다.
GitHub 대변인은 Help Net Security에 “버그 바운티를 통해 알게 됐고, 검증 후 수정을 배포했다”고 밝혔습니다. 구체적으로 미검증 Trusted Publishing 설정은 생성 후 48시간 뒤에 만료되며, 더 이상 게시를 허가하지 않습니다. 이 변경은 10월 2일에 적용됐습니다. npm 문서에도 첫 성공적인 게시가 저장소의 “불변 신원”에 바인딩된다고 나와 있습니다.
다만 GitHub 성명은 이미 한 번 이상 게시된 기존 설정이 소급 적용됐는지는 명확히 밝히지 않았습니다. Bloom 연구원 Golan Myers는 “이미 게시된 설정이 어떻게 처리됐는지는 알 수 없다”고 했습니다. 또한 GitLab Trusted Publishing 연동에도 같은 약점이 있다고 Bloom은 지적했습니다.
유지보수자가 지금 해야 할 확인 순서
패키지를 유지보수하는 사람이라면 다음을 순서대로 확인하는 것이 좋습니다.
npm trust list(또는 특정 패키지 이름과 함께)를 실행해 현재 등록된 Trusted Publisher를 봅니다.- 각 항목의 저장소가 현재 내가 통제하는 실제 저장소와 일치하는지 확인합니다. 이름이 바뀌었거나 이전됐다면 옛 이름이 남아 있을 수 있습니다.
- 이름 변경·이전된 저장소라면 해당 바인딩을 삭제한 뒤 새 저장소 이름으로 다시 설정합니다.
npm trust revoke로 기존 것을 지운 다음 새로 추가할 수 있습니다. - 앞으로 저장소를 삭제하거나 이름을 바꾸기 전에, 먼저 npm 쪽에서 해당 바인딩을 제거합니다.
- 게시된 패키지의 provenance 페이지에서 가리키는 저장소가 의도한 것인지 한 번 더 확인합니다.

이미 한 번 이상 정상 게시한 설정은 10월 2일 수정 이후에도 옛 slug에 묶여 있을 가능성이 있습니다. 그래서 “수정이 배포됐으니 괜찮다”고 넘기지 말고 직접 목록을 보는 편이 안전합니다.
일반 사용자와 AI 에이전트가 처한 위험
유지보수자가 아닌 일반 개발자나 CI 환경도 영향을 받습니다. 악성 버전이 올라가면 npm install 한 번으로 코드가 실행될 수 있습니다. Bloom은 AI 코딩 에이전트가 이 위험을 키운다고 봅니다. 텔레메트리 기준으로 일반적인 엔드포인트에서 에이전트가 한 달에 약 70개 패키지를 설치하고, 개발자 머신에서는 350개에 가깝다고 추정합니다. 이 설치는 보통 패키지나 유지보수자를 따로 검토하지 않고 일어납니다.
그래서 사용자 쪽에서는 다음을 고려할 수 있습니다.
- 중요한 프로젝트는 lockfile을 엄격히 쓰고, 새 버전을 바로 받아들이지 않습니다.
- 패키지 페이지의 provenance를 열어 저장소가 공식 프로젝트와 맞는지 확인합니다.
- postinstall 스크립트가 있는 패키지는 특히 주의합니다.
--ignore-scripts를 기본으로 쓰거나, 신뢰할 수 있는 패키지만 스크립트를 허용하는 도구를 검토할 수 있습니다. - AI 에이전트가 패키지를 설치할 때는 허용 목록이나 검토 단계를 두는 것이 좋습니다.
자주 하는 실수와 다음에 확인할 점
많은 팀이 “Trusted Publishing으로 바꿨으니 토큰 문제는 끝났다”고 생각합니다. 이번 사례는 그 가정이 완전히 맞지 않을 수 있음을 보여줍니다. 저장소 이름 변경은 흔한 일입니다. 조직 개편, 브랜딩 변경, 개인 계정에서 조직으로 이전 등이 자주 일어납니다. 그때 npm 설정을 같이 업데이트하지 않으면 옛 이름이 공격 표면으로 남을 수 있었습니다.
또 provenance가 “유효하다”는 말만 보고 안심하는 경우도 있습니다. 증명은 빌드가 특정 저장소에서 나왔다는 것만 보장합니다. 그 저장소가 원래 소유자의 것인지, 코드가 조작되지 않았는지는 별개입니다.
앞으로 GitHub와 npm이 기존 설정에 대해 소급 바인딩을 어떻게 처리했는지, GitLab 쪽 수정은 언제 적용되는지, 실제 악용 사례가 발견되는지가 추가로 확인될 부분입니다. 유지보수자는 일단 자신의 설정을 점검하고, 사용자 쪽에서는 중요한 의존성의 출처를 한 번 더 확인하는 습관을 들이는 것이 현실적인 대응입니다.
소프트웨어 공급망 보안은 한 번의 기능 개선으로 끝나지 않습니다. Trusted Publishing은 분명 진전이었지만, 신원 확인의 세부 구현이 빠지면 다시 공격 통로가 됩니다. 지금 자신의 패키지 설정을 열어보는 것이 가장 확실한 다음 단계입니다.
출처
– Help Net Security, “npm Trusted Publishing flaw exposed 1,500+ npm packages to takeover” (2026-10-13), Bloom Security 연구 및 GitHub 성명 인용
– npm 공식 문서: Trusted publishing for npm packages, npm trust 명령
– GitHub 측 48시간 만료 정책 관련 변경 (2026-10-02)