Hacker News 의견들
  • 토큰을 폐기할 때 조심해야 함. 페이로드가 dead-man's switch~/.local/bin/gh-token-monitor.sh에 설치하고, Linux에서는 systemd 사용자 서비스로, macOS에서는 LaunchAgent com.user.gh-token-monitor로 등록하는 것처럼 보임
    훔친 토큰으로 60초마다 api.github.com/user를 폴링하고, 토큰이 폐기되어 HTTP 40x가 나오면 rm -rf ~/를 실행함
    https://github.com/TanStack/router/issues/7383#issuecomment-...

    • 현실적으로 악성코드를 설치했다면 어차피 컴퓨터를 완전히 초기화해야 함
    • 놀라움. 상호확증파괴 같은 상황임
      앞으로 5년간 소프트웨어 세계는 정말 거칠어질 것 같고, 에어갭 시스템이 크게 중요해질 듯함
    • 원래 항상 백업을 설정해뒀어야 하지만, 이 사건 때문에 사람들이 백업을 갖추게 된다면 그나마 다행임
  • @mistralai/mistralai npm 패키지도 이 의 일부로 손상됨
    https://github.com/mistralai/client-ts/issues/217
    지금은 npm 레지스트리에서 내려간 상태임

  • 불행하지만, 이건 Trusted Publishing만으로는 CI에서 안전하게 배포하기에 충분하지 않다는 증거로 보임. CI 파이프라인 내부의 공격자나 훔친 저장소 관리자 권한이 있으면 쉽게 배포할 수 있음
    새 정보는 아니고, Trusted Publishing이 이를 보장하도록 설계된 것도 아니지만, 로컬 배포와 2단계 인증에서 Trusted Publishing으로 옮기면 CI 침해를 통한 이런 공격 경로가 생김. 로컬에서 작업할 때 npm publish를 막아주던 두 번째 요소가 사라지는 셈임
    현재 전개상 공격자는 CI/CD 파이프라인을 장악했고, npm publish에 두 번째 요소가 없었기 때문에 OIDC 토큰을 훔쳐 배포를 완료한 것처럼 보임. 흥미롭지만 별개로, 배포 작업 자체는 실패했는데 악성 커밋 안의 페이로드가 워크플로의 OIDC 토큰으로 스스로 배포할 수 있었던 듯함
    원하는 건 장기 토큰 없는 Trusted Publisher 모델을 유지하되, GitHub 바깥의 두 번째 요소가 남아 있는 CI 배포임. 즉 누군가 npm 쪽에서 2단계 인증으로 아티팩트를 실제 공개 상태로 승격해야 하는 단계적 배포가 필요함
    배포가 GitHub 신뢰 모델 안에서만 가능하다면 저장소 관리자 토큰을 털거나 파이프라인에 악성 코드를 넣은 사람은 누구나 배포를 쉽게 완료할 수 있음. GitHub 컨텍스트 밖의 진짜 두 번째 요소가 있으면 저장소를 망치거나 악성 코드를 심을 수는 있어도, 레지스트리용 두 번째 요소 없이는 배포하지 못함

    • 준인기 패키지 하나를 갖고 있는데, 여전히 로컬 배포와 2단계 인증을 쓰고 있음. Trusted Publishing은 너무 복잡해 보이고 계속 해킹당하는 것처럼 보여서, 우리가 안전하게 운용하기엔 너무 복잡한 게 아닌가 싶고 다시 설계부터 해야 할지도 모름
    • 여전히 Trusted Publishing은 큰 개선이라고 생각하지만, 릴리스를 진짜 공개 상태로 표시할 때 두 번째 요소를 요구하는 아이디어는 좋음. 그러면 이런 CI 웜을 실행하기가 매우 어려워질 것임
    • YubiKey 같은 걸로 터치 서명을 하고 싶음. 클라우드가 대신 자격 증명을 관리하도록 믿는다는 발상 자체가 실수처럼 보임
    • astral 블로그에서 최근 Trusted Publishing을 쓰면서도 릴리스 게이트를 어떻게 두는지, 즉 릴리스 워크플로에 수동 승인을 넣는 방식을 보여줬음. 안타깝게도 NPM/PyPI/Rubygems의 Trusted Publishing 문서는 이 가능성을 언급조차 하지 않고, 기본값으로 제공하지도 않음
    • 이런 종류의 공급망 공격에 Trusted Publishing이 어떤 차이를 만든다고 사람들이 말하는 이유가 항상 이해되지 않았음
  • 사후 분석: https://tanstack.com/blog/npm-supply-chain-compromise-postmo...

    • TanStack의 사후 분석은 고맙지만, npm 생태계 전체 관점의 보안 이슈는 아직 진행 중인 우려가 맞지 않나 싶음
      TanStack 패키지를 가져오거나 포함했을 수 있는 하위 패키지들을 안전하다고 봐도 된다는 증거가 있는지 궁금함
  • postinstall 스크립트는 치명적임. 모두 pnpm을 써야 함
    FORK에 푸시된 “고아” 커밋이 npm 클라이언트에서 이런 일을 유발할 수 있다는 게 말이 안 됨. GitHub에도 책임이 크다고 봄. 악의적인 포크의 커밋이 GitHub의 공유 객체 저장소를 통해 정상 저장소와 구분되지 않는 URI로 접근 가능하다는 건 완전히 미친 구조임

    • 업데이트된 의존성으로 앱을 실행하면 어차피 그 코드가 실행됨. root냐 non-root냐도 중요하지 않고, 중요한 것들은 애플리케이션을 실행하는 사용자 권한으로 접근 가능함
    • 이게 어떻게 GitHub의 P0 장애가 아닌지 모르겠음. 설명할 수 있는 사람이 있나?
      처음 읽었을 때는 “fork”라는 말을 잘못 써서 사실 공식 저장소의 브랜치를 뜻하는 줄 알았음. 그게 진짜일 리 없다고 생각했는데, 세상에
  • https://tanstack.com/blog/npm-supply-chain-compromise-postmo...
    TanStack에서 이 사건에 대한 사후 분석을 방금 공개했음

  • npm 환경을 안전하게 설정하라는 알림임
    https://gajus.com/blog/3-pnpm-settings-to-protect-yourself-f...
    설정 몇 개만으로 큰 문제를 줄일 수 있음

    • npm v11 이상에서는 allow-git=none도 있음: https://github.blog/changelog/2026-02-18-npm-bulk-trusted-pu...
    • 이 글은 npm의 최소 릴리스 나이에 대해 틀린 것 아닌가 싶음. 1) 설정 이름은 min-release-age임. 2) 무슨 이유인지 분 단위가 아니라 일 단위로 만들었음: https://docs.npmjs.com/cli/v11/using-npm/config#min-release-...
      의존성 관리자 공간이 완전히 불필요하게 파편화됐다고 봄
    • 최소 나이를 7일로 설정하면 npm 공급망 취약점을 “절대” 겪지 않는다는 주장은 과함
    • 모든 의존성을 반드시 고정해야 함
      패키지 버전 의존성이 ^1.0.0처럼 되어 있거나 심지어 "*"라면 더 읽지 말고 즉시 안전한 버전으로 고정해야 함
  • Claude로 급하게 만들어서 확산을 줄이는 데 도움을 주려 했음. 당연히 직접 검증해야 하지만, 언급된 손상 패키지가 머신에 있는지 스캔해줌: https://github.com/PaulSinghDev/tanstack-shai-hulud-fix

  • 이제는 모두가 각 프로젝트를 개별 VM에서 실행해야 하는 단계에 온 것 같음
    최근의 로컬 권한 상승 취약점을 보면 Docker만으로는 절대 충분하지 않음. 애초에 컨테이너는 주된 보안 경계로 설계된 것도 아님

    • Devcontainers가 이런 “격리된 개발 환경” 개념의 가장 잘 알려진 형태지만, 완전한 VM은 아니고 이번 일에서 완전히 보호해주지도 못함. GitHub 자격 증명이 컨테이너 안으로 자동으로 들어오기 때문임
      컨테이너 안에서 접근해야 하는 다른 클라우드 서비스가 있다면, 이 자격 증명 탈취기가 그것도 가져갈 것임. 그래도 피해 반경은 줄여주니 최소한 개선은 됨
    • QubesOS가 올바른 방향을 잡았음. 루트에 여러 VM을 두고 겹겹의 보안 계층을 원하게 됨
    • 컨테이너를 꼭 쓰겠다면 컨테이너마다 VM을 하나씩 두는 방법도 있음. 임의의 Kubernetes 서비스가 아니라 전부 VM에서 돌린 덕분에 최근 몇 주는 꽤 편안했음
    • 다행히 C와 C++처럼 더 안전한 언어 생태계를 쓰는 프로젝트는 이런 문제에서 벗어나 있음 :-)
  • 와, 또 다른 거대 패키지임. Axios와 LiteLLM이 손상된 뒤 올렸던 공익 알림을 다시 올림. 수명주기 스크립트 관련 내용도 적용됨
    npm/bun/pnpm/uv는 이제 모두 패키지의 최소 릴리스 나이 설정을 지원함. ~/.npmrcignore-scripts=true도 넣어뒀고, 분석상 이것만으로도 취약점을 완화할 수 있었음. bun과 pnpm은 기본적으로 수명주기 스크립트를 실행하지 않음
    전역 설정으로 최소 릴리스 나이를 7일로 설정하는 방법은 다음과 같음
    ~/.config/uv/uv.toml
    exclude-newer = "7 days"
    ~/.npmrc
    min-release-age=7 # days
    ignore-scripts=true
    ~/Library/Preferences/pnpm/rc
    minimum-release-age=10080 # minutes
    ~/.bunfig.toml
    [install]
    minimumReleaseAge = 604800 # seconds
    전역 설정을 덮어써야 한다면 CLI 플래그를 쓰면 됨
    npm install --min-release-age 0
    pnpm add --minimum-release-age 0
    uv add --exclude-newer "0 days"
    bun add --minimum-release-age 0
    한 가지 더 덧붙이면, 의존성 대기 시간을 대규모로 도입하면 취약점 발견이 늦어지거나, 의존성 대기 시간이 일종의 무임승차라는 우려가 있는 듯한데 동의하지 않음. 의존성 대기 시간으로 교환하는 것은 시간 선호이고, 항상 나보다 시간 선호가 높은 사람들은 존재함
    0: https://news.ycombinator.com/item?id=47582220
    1: https://news.ycombinator.com/item?id=47513932

    • 동의함. 지난 두 번의 파도가 오기 전인 3월에 이 설정들을 켜둬서 다행임. 추가로 저장소에 lockfile을 커밋해두고 새 의존성을 추가할 때 주의해야 함
      예기치 않은 변경을 피하려면 pnpm install --frozen-lockfile을 쓰면 됨. min-release-age를 설정하지 않았다면 간접 의존성을 통해서도 영향을 받은 패키지를 끌어올 수 있다는 점을 기억해야 함. 가능하면 패키지 관리자 버전도 고정하는 게 좋음