안전한 설치를 위한 npm 보안 모범 사례

지원 종료(EOL)된 PHP용 php2shell 패치(CVE-2026-109618 및 CVE-2026-109619)가 출시되었습니다. 수요가 많아 평소보다 영업팀의 답변이 늦어지고 있으므로, 패치된 패키지를 가장 빠르게 확보하려면 온라인에서 Runtimes용 ELS를 구매하시기 바랍니다 (새 탭에서 열림). 패치 FAQ 읽어보기

이 블로그 글의 AI 요약으로 바로 가기

  • Perplexity를 사용하여 이 게시물을 요약해 주세요
  • Gemini로 이 게시물을 요약해 주세요

매번 npm install 은 신뢰의 행위입니다. 자바스크립트 개발을 가속화하는 바로 그 패키지 생태계가, 동시에 공급망 공격의 주요 경로이기도 합니다. 설치 명령어를 실행하면 방대한 의존성 트리를 가져오게 되는데, 그중 상당수는 잘 알려지지 않았거나 숨겨져 있습니다.

npm install 은 대부분의 경우 안전합니다. 여기서 ‘대부분’이라는 표현이 많은 의미를 담고 있습니다. install은 단순한 다운로드가 아니라 실행 과정이며, 의존성 라이프사이클 스크립트는 설치 환경이 이미 가지고 있는 권한으로 실행됩니다. 개발자의 노트북이나 CI 러너에서는 대개 이 권한만으로도 충분합니다. 코드는 누구도 검토하기 전에 이미 가져와 압축이 풀리고 실행되고 있습니다. 아래의 모든 내용은 2026년 8월 기준 npm v12, pnpm 11, Yarn 4.10을 반영한 것입니다. 이 분야의 기본 설정이 빠르게 변화해 왔기 때문입니다.

타이포스쿼팅의 함정

이 공격은 공격자가 오타가 포함된 패키지를 공개할 때 발생합니다. 오타는 단순히 패키지 이름의 철자가 틀린 것일 수도 있고, 완전히 다른 형식상의 속임수일 수도 있습니다. 이 패키지는 인기 있는 패키지와 거의 똑같이 보이며, 동일한 기능을 재현합니다.

  • 문자 조작: 교환 lodash 를 lodahs로 바꾸거나, 동형 문자(예: ‘l’을 ‘I’로 대체)를 사용하는 방법.
  • 서식 변형: 하이픈 활용법 (cross-env 대 crossenv) 또는 복수형 표기(color vs. colors) 또는 복수형화 (colorvs.) 등이 있습니다.
  • 네임스페이스/범위 혼동: 공식 라이브러리를 모방한 악성 스코프 기반 패키지 생성.

이러한 패키지는 설치 과정에서 임의의 코드를 실행할 수 있으며, API 키, 환경 변수, AWS 인증 정보 등 모든 유형의 인증 정보를 유출합니다. 이러한 일은 개발자가 코드를 검토할 기회조차 갖기 전에 극히 빠르게 발생할 수 있습니다.

의존성 혼동 공격의 작동 원리

이 공격은 공개 npm 레지스트리와 함께 내부 레지스트리를 병행하여 운영하는 하이브리드 환경을 악용합니다.

의존성 혼동(Dependency Confusion) 공격은 기업 내부에 특정 패키지가 존재할 때, 공격자가 공개 레지스트리에 동일한 이름의 패키지를 등록할 때 발생합니다. 공격자는 대개 내부 패키지의 버전 번호보다 높은 버전 번호를 부여하여 패키지를 등록합니다. 빌드 환경이 두 레지스트리 모두에서 해당 이름을 조회할 수 있는 경우, 공격자가 등록한 공개 패키지를 대신 불러올 수 있습니다. 이 패키지에는 악성 코드가 포함되어 있습니다.

이 현상이 발생하는지 여부는 레지스트리, 범위, 프록시 및 잠금 파일 설정에 달려 있습니다. 문제가 발생하는 설정은 아무도 검토하지 않은 것들입니다.

이러한 위험을 완화하는 방법은 스코프가 지정된 패키지를 사용하고 라우팅을 결정론적으로 만드는 것입니다. 각 비공개 스코프를 .npmrc에서 각 비공개 스코프를 비공개 레지스트리에 명시적으로 매핑하고, 프록시 체인 내의 어떤 요소도 사용자 모르게 공용 레지스트리로 되돌아가지 않도록 해야 합니다. npmjs.com 에서 일치하는 스코프를 선점하면 공격자가 여러분이 이미 소유한 스코프 아래로 패키지를 게시할 수 없기 때문에 보안에 추가적인 보호 계층이 생깁니다. 지난 몇 년 동안 패키지를 설치해 본 적이 있다면, 스코프 지정 패키지가 무엇인지 잘 알고 계실 것입니다. 스코프 지정 패키지는 대략 다음과 같은 형태를 띱니다. @my-org/cool-package와 같습니다. 첫 번째 문자는 @ 기호이며, 그 뒤에는 사용자 이름이나 조직명, 그리고 설치하려는 패키지명이 이어집니다.

이러한 안전장치가 없다면, 스코핑만으로는 공격을 완전히 막을 수 없습니다. 스코프 자체는 명명 규칙일 뿐, 제어 수단이 아닙니다. CI/CD 플랫폼이 올바르게 구성되었는지 확인하는 것도 위험을 완화하는 데 도움이 됩니다.

유지보수 담당자 계정이 해킹당하면 어떻게 될까요?

타이포스쿼팅과 종속성 혼동은 모두 사용자가 의도하지 않은 프로그램을 설치하도록 유도합니다. 더 교묘한 방식은 이 단계를 건너뜁니다. 공격자는 사용자가 이미 신뢰하고, 검토를 마쳤으며, 고정해 둔 패키지를 장악합니다.

현재 가장 큰 피해를 입히고 있는 공격 경로는 바로 이것입니다. 2025년 9월, npm 유지보수 담당자들을 대상으로 한 피싱 공격으로 ‘Shai-Hulud’ 웜이 생성되었는데, 이 웜은 클라우드 토큰을 탈취한 뒤 이를 이용해 다른 계정으로 확산되었습니다. 2026년 8월 4일, 한 공격자가 keyv(주간 다운로드 수가 1억 건을 훌쩍 넘는 라이브러리)의 유지보수자가 관리하는 GitHub 계정에 침입하여, keyv, cacheable, flat-cache, 파일 항목 캐시등 해당 클러스터에 속한 패키지들이 포함되었습니다. 추적 결과, 하루 만에 수백 개의 영향을 받은 패키지명과 천 개가 넘는 영향을 받은 버전이 집계되었습니다.

여기서 여러분이 우려해야 할 부분이 바로 이것입니다. 이번 릴리스는 유지보수자가 직접 관리하는 GitHub Actions 워크플로를 통해 배포되었기 때문에, 유효한 OIDC 기반 출처 정보와 유효한 무결성 해시를 포함하고 있었습니다. 이 두 가지 정보 모두 정식 워크플로에서 생성된 정상적인 아티팩트와 정확히 일치했습니다. 출처 정보는 패키지가 어디서, 어떻게 빌드되었는지를 확인해 줍니다. 하지만 누가 키를 보유하고 있었는지에 대해서는 아무런 정보를 제공하지 않습니다.

이번 사례는 암호화 검사가 아니라 행동 기반 도구와 릴리스 버전 관리 기능을 통해 적발되었습니다. 출처 추적 기능은 갖춰둘 가치가 있지만, 그것이 전부는 아닙니다.

왜 전이적 종속 관계는 감사 대상에서 제외되는가

직접 의존성은 비록 검토를 거쳤더라도, 여러분이 보지 못했고 인지하지 못한 수많은 전이적 의존성을 수반할 수 있습니다. 이러한 숨겨진 의존성은 의존성 트리 내에서 5~10개 이상의 계층 깊숙이 자리 잡고 있을 수 있습니다.

대부분의 종속성은 프로젝트 트리 깊숙한 곳에 위치해 있어 수동으로 검토되는 경우가 거의 없으며, 이로 인해 공격자에게 완전히 노출된 ‘어두운 동굴’이 형성됩니다. 이러한 가시성 부족은 공급망 공격의 주요 경로입니다. 공급망 공격은 악의적인 행위자가 패키지에 침투하거나 패키지를 완전히 삽입할 때 발생하며, 설치 과정에서 종종 눈에 띄지 않습니다. 이는 제 Dockerfile 블로그 게시물에서 언급한 바와 같이, 일부 AI 기반 스캐폴딩 도구조차도 이 위험을 해결하지 못하는 경우가 있습니다.

한편 npm ls 는 전체 의존성 트리를 시각화하는 데 유용한 도구이지만, 취약점이나 의심스러운 행동을 스캔하지는 않습니다. 그렇기 때문에 스택 깊숙이 숨겨진 문제를 찾아내는 데는 소프트웨어 구성 분석(SCA) 도구가 중요합니다.

도구 잘하는 것 그리울 거예요
Dependabot 트리 내의 알려진 CVE, 자동화된 버전 번호 증가 PR 아직 CVE가 등록되지 않은 완전히 새로운 악성 패키지들
소켓 행동 신호, 새로운 설치 스크립트, 예기치 않은 네트워크 또는 파일 시스템 접근 처음부터 CVE 데이터베이스로 구축된 것은 아니다
Snyk CVE 대응 및 공급망·라이선스 점검 출력이 잡음 상태가 아니게 되려면 조정이 필요합니다.

GitHub에 저장소가 있다면 Dependabot은 사용하기에 가장 번거로움이 적은 기본 솔루션입니다. 다만 이를 악성 패키지 탐지 기능으로 오해해서는 안 됩니다. 1시간 전에 공개된 패키지에는 대조할 CVE가 없기 때문입니다.

얼마나 악의적인가 사전 설치된 및 설치 후 스크립트가 코드를 실행하는가

사전 설치 및 postinstall 훅을 package.json 의preinstall및postinstall

이 스크립트들은 은행 시스템, 빌드 서버 등과 같은 고가치 자산을 표적으로 삼습니다. 말하자면 ‘최상위 자산’들이죠. 이 스크립트들은 다음과 같은 중요한 인증 정보를 유출합니다. .env 파일, AWS 토큰, SSH 키와 같은 핵심 인증 정보를 유출하는데, 이는 우리가 끊임없이 주의를 기울여야 한다는 점을 상기시켜 줍니다 절대 .

일부 스크립트는 페이로드를 실행하기 전에 샌드박스나 엄격하게 모니터링되는 시스템을 탐지하기 위해 환경 검사를 수행하며, 보안 수준이 낮은 환경을 우선적으로 공격 대상으로 삼습니다.

이는 여전히 가장 선호되는 유포 수단입니다. 2026년 8월 캠페인에서 발견된 모든 감염된 버전에는 사전 설치용 드롭퍼를 포함하고 있었습니다.

하지만 마침내 기본 설정이 변경되었습니다. pnpm은 v10부터 기본적으로 종속성 라이프사이클 스크립트 실행을 중단했으며, 2026년 7월 8일에 출시된 npm v12도 동일한 방식을 채택하여 npm approve-scripts를 통해 관리됩니다. 승인되지 않은 스크립트는 경고와 함께 건너뛰어지므로, strict-allow-scripts 를 설정하세요. npm 11 이하 버전에서는 이 플래그가 자동으로 해당 기능을 수행합니다:

bash

npm install --ignore-scripts

이 모든 내용이 import 시점에 실행되는 코드에는 전혀 영향을 미치지 않는다는 점을 알아두면 좋습니다. 앱에서 require() 를 호출할 때 최상위 코드가 실행되는 침해된 패키지는 설치 스크립트의 제어를 완전히 우회합니다.

패키지 연도가 보안 지표가 되는 이유

이러한 공격의 대부분은 실제로 적발되는데, 대개 몇 주가 아니라 몇 시간 내에 적발됩니다. 문제는 그 몇 시간 동안 CI가 이미 문제가 있는 버전을 가져왔다는 점입니다.

최소 릴리스 연령을 설정하면 별도의 검토 절차 없이도 해당 기간을 좁힐 수 있습니다. 현재 모든 주요 패키지 관리자가 이 기능을 제공하고 있습니다. pnpm 11은 minimumReleaseAge 값을 1,440분으로 설정하여 기본적으로 활성화하며, Yarn 4.10은 기본적으로 npmMinimalAgeGate 값을 3일로 설정하며, npm에는 min-release-age 를 지원하지만 비활성화되어 있으므로 직접 설정해야 합니다. npm은 일 단위로, pnpm은 분 단위로 계산하므로 단위에 유의하십시오.

# .npmrc

min-release-age=7

대부분의 실무자들은 3일에서 7일 사이를 기준으로 삼습니다. 하지만 이는 처음부터 악성이었던 패키지나, 설정된 임계값을 넘어서도 비활성 상태로 남아 있는 패키지에는 아무런 효과가 없습니다.

npx 도 같은 고려가 필요합니다. 새로운 버전을 가져올 때, lockfile이 어떤 버전을 선택할지 제어하지 않으므로, 다음 명령어로 호출할 때 버전을 고정해야 합니다. npx [email protected]를 사용하여 호출할 때 버전을 고정해 두세요.

npm 패키지를 안전하게 설치하는 방법

  • 잠금 파일을 반드시 커밋하고 다음 명령어를 사용하세요. npm ci 를 사용하여 CI 환경에서 결정론적이고 안정적인 빌드를 수행하십시오. 락파일과 npm ci 를 함께 사용해야만 전이적 종속성을 포함하여 해결된 트리가 실제로 고정됩니다.
  • 다음 위치에 정확한 버전을 지정하세요 package.json 에서 접두사 기호(^, ~)를 제거하여 직접 종속성에 대한 업그레이드 시점을 제어할 수 있습니다. 이는 전이적 트리에는 아무런 영향을 미치지 않으므로, 안전성보다는 예측 가능성을 확보하는 데 도움이 됩니다.
  • 패키지 관리자가 기본적으로 종속성 스크립트를 차단하도록 합니다. 이는 이미 pnpm 10 버전 이상과 npm v12에서는 이미 이러한 동작을 따르고 있으며, 이는 –ignore-scripts 옵션을 사용하면 이전 버전에서도 동일하게 적용됩니다.
  • 패키지를 정기적으로 점검하고, Dependabot, Socket, Snyk와 같은 SCA 도구를 활용하여 의존성 트리에서 이상한 점을 찾아내세요.
  • 최소 공개 기간을 3일에서 7일로 설정하십시오.
  • 게시 권한이 있는 모든 계정에 대해 MFA를 활성화하고, 게시 토큰의 범위를 좁게 설정하며 유효 기간을 짧게 유지하십시오.

스크립트를 차단하는 데는 실질적인 비용이 따르므로 신중하게 결정하십시오. 네이티브 모듈과 브라우저 자동화 도구를 사용하려면 별도의 설치 절차가 필요하며, 정교한, bcrypt, Prisma, Playwright, Cypress 등이 주로 차단 대상이 됩니다. CI 환경에서는 기본적으로 차단하고, 필요한 것만 허용하며, 프로덕션 자격 증명이 포함되지 않은 환경에서 해당 빌드를 실행하세요. 노트북에서는 모든 보호 기능을 해제하기보다는 오류가 발생하는 패키지 하나만 허용하도록 하세요. 어느 쪽이든 무증상 오류에 주의해야 합니다. 차단된 네이티브 빌드는 오류 없이 종료될 수 있지만, 나중에 런타임 시 모듈 누락 문제로 드러날 수 있기 때문입니다.

npm 공급망 보안에 대한 마무리 생각

소프트웨어 공급망 보안은 단 한 번만 처리하면 끝나는 운영 요건이 결코 아닙니다. 보안 관행은 지속적으로 이루어져야 하며, 항상 최우선으로 고려해야 합니다. 큰 문제가 터지기 전까지는 뒷전으로 밀려나는 세 가지 요소가 있는데, 바로 성능, 접근성, 그리고 – 짐작하셨겠지만 – 보안입니다. 보안을 유지하는 데 도움이 되는 도구와 기법을 꾸준히 파악하는 것은 개발자로서의 의무 중 하나입니다. 실용적인 방어 수단을 염두에 두고, 제품을 출시할 때는 항상 보안을 최우선으로 삼아야 합니다.

 

요약
안전한 설치를 위한 npm 보안 모범 사례
기사 이름
안전한 설치를 위한 npm 보안 모범 사례
설명
npm install을 실행할 때마다 코드가 실행됩니다. 타이포스쿼팅, 의존성 혼동, 관리자 계정 탈취가 어떻게 이루어지는지, 그리고 패키지를 설치하는 방법을 알아보세요.
작성자
게시자 이름
TuxCare
게시자 로고

4,500명 이상의 Linux 및 오픈 소스 전문가와 함께하세요!

한 달에 두 번. 스팸이 없습니다.

목차
필요한 오픈 소스 보안에 대한 답변 얻기

4,500명 이상의 Linux 및 오픈 소스 전문가와 함께하세요!

한 달에 두 번. 스팸이 없습니다.