Red Hat Cloud Services 전반에서 악성 npm 패키지 발견
요약
Red Hat Cloud Services 관련 npm 공급망 공격 사례를 통해 패키지 릴리스 지연(Cooldown)의 중요성을 강조합니다. pnpm의 사례와 depsguard.com 같은 도구를 소개하며, 보안 수정과 일반 릴리스를 분리하는 체계적인 배포 전략을 제안합니다.
핵심 포인트
- 악성 패키지 방지를 위해 신규 릴리스에 대한 '쿨다운' 설정 권장
- pnpm은 기본적으로 1일 쿨다운을 제공하여 공급망 보안 강화
- 보안 수정과 일반 기능 업데이트를 분리하여 처리하는 체계 필요
- MFA 및 Staged Publishing을 통한 배포 파이프라인 보호 중요성
쿨다운 설정 얘기로 스레드를 다시 빌려도 괜찮길 바람. axios, tanstack, @redhat-cloud-services와 최근의 여러 npm 공급망 공격은 쿨다운이 있었으면 막을 수 있었을 것임
Artifactory/Nexus를 쓰면 이미 있을 가능성이 크고, 없더라도 설정은 쉬움. npm이나 PyPI 침해 대부분은 몇 시간 안에 내려갔으니, 릴리스된 지 N일 미만인 패키지를 무시하는 방식이면 충분함. 1일도 효과가 있고, 3일은 괜찮으며, 7일은 다소 과하지만 동작함
최신 pnpm은 기본으로 1일 쿨다운을 넣었음: https://pnpm.io/supply-chain-security
한 번 클릭으로 끝내고 싶으면 https://depsguard.com을 쓸 수 있음. npm, pnpm, yarn, bun, uv, dependabot에 쿨다운과 권장 설정을 추가하는 CLI이며, 내가 유지보수자임
쿨다운에 더 집중한 https://cooldowns.dev도 있고, 로컬 설정을 돕는 스크립트도 있음. 모두 오픈소스/무료임 ~/.npmrc 등을 직접 편집할 줄 알면 굳이 필요 없지만, 주변에 원클릭 수정만 필요한 사람이 있다면 다음 공격을 피하는 데 도움이 될 수 있음
단, 새 치명적 CVE를 패치해야 할 때는 쿨다운을 우회해야 하며, 각 도구에 우회 방법이 있음. 최근 몇 달은 정확한 수치는 없지만, Mythos식 취약점 발견 시대에도 새 제로데이 CVE보다 악성 버전 배포 같은 소프트웨어 공급망 공격 쪽 위험이 더 커 보임
임베디드 개발자로서 툴체인과 의존성을 몇 년씩 고정하는 데 익숙하다 보니, 웹 개발 문화는 여러 면에서 충격적임
~/.npmrc 파일을 열고 한 줄 추가할 수 있는 사람인데도 원클릭 수정이 필요한 경우는 아주 작은 집단처럼 느껴짐
쿨다운과 더불어 더 많은 패키지 관리자가 보안 수정과 일반 릴리스(버그 수정/성능 개선/새 기능)를 구분해 처리하면 좋겠음
“보안 수정은 보안 수정만 포함해야 하며 다른 기능을 실으면 안 된다”고 말하는 건 충분히 가능함. 그러면 보안 연구자와 도구 모두가 감사하기 쉬워짐
일반 릴리스에는 쿨다운을 적용하고, 보안 수정에는 쿨다운을 없애거나 훨씬 짧게 둘 수 있음
Debian처럼 매우 안정적인 서버를 두고, unattended upgrades를 보안 수정에만 적용하도록 설정할 수 있는 체계는 참고할 만함. 이런 새 패키지 릴리스는 보안 연구자가 감사하기도 더 쉬움
이런 스레드마다 이 공격 유형이 npm에만 있는 것처럼 굴거나, 아무 조치도 없었던 것처럼 비꼬는 댓글이 많지만, 그건 공정하지 않다고 봄
지연선(delay line)과 pnpm 등이 패키지 소비자를 보호하려고 도입한 좋은 기능들을 언급하는 댓글도 많음
덜 이야기되는 부분은 패키지 유지보수자 쪽 도구임. 배포용 MFA: https://docs.npmjs.com/requiring-2fa-for-package-publishing-..., 약 1년 전부터 제공된 trusted publishers: https://docs.npmjs.com/trusted-publishers
최근에는 두 기능의 장점을 합친 staged publishing도 나왔음: https://docs.npmjs.com/staged-publishing
이제 정적 자격 증명 없이 CI에서 배포하고, 레지스트리에 실제 공개되기 전 유지보자가 MFA로 승인하도록 요구할 수 있음. 원하면 GitHub Actions Environments 보호로 CI 쪽에서 다중 승인이나 시간 지연도 요구 가능함
커뮤니티가 이런 배포 보호 장치를 채택하도록 장려해야 하며, 그렇지 않으면 이 문제는 계속될 것임
[1]에 따르면 “영향받은 모든 패키지는 RedHatInsights/javascript-clients 저장소에서 GitHub Actions OIDC를 통해 배포됐고, 이는 상류 CI/CD 파이프라인 자체가 침해됐음을 나타낸다”고 함
그러면 악성 패키지도 초록색 별을 받고, 사용자에게 “출처 증명과 함께 빌드되고 서명됨”이라고 안심시켰을 것임
[1] https://lwn.net/Articles/1075742/
계속 벌어지고 있으니 웃기긴 함. npm 공격은 달력에 표시할 수 있을 정도고, 누군가 The Onion의 고전 “피할 방법이 없다” 기사 패러디를 npm 버전으로 만들기도 했음
막기 위한 작업이 진행되는 건 훌륭하지만, 그래도 계속 일어남. “또 시작이네” 싶은 의미에서 웃김
모두에게 필수로 만들면 그때야 뭔가 한 셈이 됨
기계적인 변경에는 별로 감명받지 않고, 이 문제를 문화적 문제로 보는 집단이 있는 것 같음
밖에서 보면 웹 개발은 정신없는 서부 개척지 같은 에너지가 있음. 가변성, 동적 타입, 계속 바뀌는 표준과 프레임워크, 지속 배포, CDN, 실시간 A/B 캠페인, 많은 의존성, 여러 인프라에 퍼진 민감한 사용자 데이터가 있음
이 관점이 정확하다고 말하는 건 아니고 “거봐라” 식 태도가 맞다고도 생각하지 않지만, 어디서 나오는지는 이해됨
내 생각엔 둘 다 돼지에 립스틱 바르는 해결책임. 결국 전부 “릴리스를 더 어렵게 배포하게 만들자”의 변형이고, 사람들에게 우회법만 학습시킬 뿐임
특히 둘 중 어느 것도 xz-utils 백도어가 패키지 배포에 들어가는 걸 막지 못했을 것임. xz-utils는 정교한 상류 침해의 기준점으로 남아 있음
여기서 버그는 이미 신뢰한 상류를 더 잘 인증해야 한다는 게 아니라, 상류를 보안의 유일한 출처로 신뢰할 수 없다는 것임. 상류는 견고한 릴리스 엔지니어링에 관심도 적고 잘하지도 못할 해커들의 집단임
하지만 그걸 잘하는 사람들도 있음. Linux 세계의 해법이자 xz-utils에서 우리를 구한 방식은, 해커가 만든 상류를 사용자를 위해 검토·감사·패키징·커스터마이즈하는 두 번째 인간 계층이 있다는 것임
이 사람들은 다른 눈, 다른 소비자 요구, 다른 품질 기준을 갖고 있고, 상류가 잡을 준비가 안 된 버그와 악의를 잡아냄
NPM, cargo, PyPI 등은 이 인간 노동 요구를 우회할 수 있다고 계속 생각하지만, 그럴 수 없음. NPM 생태계는 특히 매우 빠른 릴리스, 느슨한 호환성 요구, 극단적인 재사용에 익숙한 웹 개발자들이 많아 node 패키지에서 Python이나 Rust보다 이런 일이 더 자주 보이는 이유가 됨
모든 패키지를 항상 최신이자 최고 상태로 유지해야 한다는 발상은 다시 생각해볼 만함. 모든 마이너 버전 업데이트를 즉시 적용할 필요는 없고, 높은/치명적 취약점도 꼭 마이너 버전 업그레이드일 필요는 없을 수 있음
모두가 3일 지연을 시작하면, 결국 모두가 3일째에 발견하게 되는 것 아닐까?
몇 가지 제안이 있음. 의존성 쿨다운 1~2일은 CVE 패치 능력을 해치지 않으면서 매우 효과적인 듯함 npm install, npm test처럼 코드가 실행되는 곳은 모두 권한 없는 환경에서 돌아가야 함. GitHub Actions에서는 아티팩트를 빌드·테스트하는 작업과 배포·서명 등을 하는 작업을 분리하면 비교적 간단함. AI를 쓴다면 이 패턴을 강제하는 skill/가이드를 추가하면 됨
GitHub Actions를 쓴다면 최신 zizmor를 설치하면 보안 태세가 크게 좋아짐
두 번째 조치는 더 이상 “웜처럼 전파 가능”하지 않게 해주며, 지금 문제의 큰 부분을 줄여줌. 첫 번째는 회사들이 공격에 대응할 시간을 벌어줌. 이 분야의 몇몇 벤더도 평가해볼 만함
zizmor가 침해되면 어떻게 하나?
새 패키지는 지연해야 한다고 말한 직후라 농담처럼 웃겼음
이런 쿨다운 대신 격리된 컨텍스트에서 빌드를 실행하면 되는 것 아닐까?
로컬에서 Maven 프록시를 돌리고, 모든 빌드는 컨테이너 안에서 함. Python, npm, Go는 공개 저장소만 쓰므로 이 빌드들도 컨테이너에서 하되 저장소 프록시는 필요 없음
코드가 실행되는 모든 곳이라면 codex, claude-code 같은 에이전트형 오케스트레이터들이 기본으로 그렇게 하는 것 같음
자동화 못 할 일은 아님. Go 쪽에서는 이를 vendoring이라고 부를 수 있음: https://go.dev/ref/mod#vendoring
제3자 의존성 호스팅 업체 의존을 줄이거나 덜어내고, 의존성을 자기 코드 리뷰 도구 안으로 가져오며, 장기적으로 재현 가능한 빌드를 보장하는 데 좋음
문제는 의존성의 의존성, 그리고 그 아래 여러 단계까지 계속 이어진다는 것임
CLI에서 의존성을 쉽게 감사하려고 Packj를 만들었음
Packj(https://github.com/ossillate-inc/packj)는 행위 분석으로 악성 PyPI/NPM/Ruby/PHP 등의 의존성을 탐지함. 정적+동적 코드 분석으로 셸 실행, SSH 키 사용, 네트워크 통신, decode+eval 사용 같은 침해 지표를 스캔함. 오타 스쿼팅 같은 악성 행위자를 찾기 위해 여러 메타데이터 속성도 확인함
확률은 바꿀 수 있겠지만, 성실하게 포크하고 앞으로의 모든 취약점에 monkeypatch하지 않으면 침해된 포크를 영원히 배포하게 될 수도 있음
패키지를 최신으로만 두지 말고 버전 번호도 제한해야 하지 않나?
약 일주일 전에 노트북에서 Node를 지웠고, 기분이 좋았음
운 나쁘게 익스플로잇을 맞더라도 피해 반경을 줄이려고 모든 작업을 개발 컨테이너나 다른 샌드박스에서 하려는 중임. 공격자가 Claude 토큰을 가져갈 수는 있겠지만, 컨테이너를 쉽게 탈출해 홈 디렉터리를 훑지는 못할 것임
쿨다운과 설치 스크립트 허용 목록은 특히 CI에서 계층형 보안에 좋은 추가 장치임. 하지만 근본적으로 바뀌어야 할 것은 운영체제 권한 모델이라고 봄. 서드파티 소프트웨어를 사용자 권한 전체로 신뢰하는 기본값은 더 이상 작동하지 않음
Bubblewrap/Firejail/Flatpak 같은 걸 쓰는지, 아니면 그런 설정이 어떤 모습인지 궁금함. 비슷한 아이디어를 한동안 생각만 하고 아직 실행은 못 했음
순수 npm(v11.10.0 이상)을 쓰면 프로젝트 루트의 .npmrc에 그냥 min-release-age=5를 추가하면 됨
Yarn 4는 이걸 자동화할 수 있음
NPM은 설계부터 망가졌고, 커뮤니티에 만연한 NIH 증후군 때문에 단순한 조치도 못 하게 됨
두 번째 문장은 잘 이해가 안 됨. npm은 ‘여기서 만든 게 아니면 안 씀’의 반대 문제 아닌가?
자체 개발보다 외부 패키지를 많이 받아들이다 보니 npm 프로젝트는 크고 복잡한 의존성 트리를 갖는 경향이 있음. https://www.npmjs.com/package/is-windows 같은 패키지는 같은 코드를 직접 쓰기 너무 쉬운데도 잠재 취약점과 유지보수 골칫거리를 만든다는 불만이 오래전부터 있었음
NIH 쪽에서 흔한 오류는 X 패키지를 다시 만드는 데 시간이 많이 걸릴 거라는 생각임
하지만 당연히 모든 기능을 다시 만들 필요는 없고, 필요한 기능만 만들면 됨
게다가 기능 하나만 코딩할 때는 추상화나 추가 함수 인터페이스를 만들 필요도 없음. 그래서 더 싸고, 아마 더 잘 통합됨
또 다른 오류는 버그와 취약점을 만들 거라는 생각임. 나쁜 프로그래머라면 그럴 수도 있지만, 서로 정확히 맞도록 설계되지 않은 두 라이브러리의 통합 경계에서 생기는 취약점 범주는 피할 수 있음. 그런 경우가 많음
AI 자동 생성 콘텐츠
본 콘텐츠는 GeekNews의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기