npm 패키지 공급망 공격, IT 에이전시가 반드시 알아야 할 보안 대응법 (id.news.hada.io)
목차(6)
한줄 요약
npm 공급망 공격은 외주 개발 환경의 CI/CD와 클라이언트 자격증명을 동시에 위협한다.
본문
공급망 공격(Supply Chain Attack)은 개발팀이 직접 작성하지 않은 외부 패키지를 통해 악성 코드가 빌드 환경 전체로 확산되는 보안 위협이다. 최근 들어 npm 생태계를 겨냥한 이런 유형의 공격이 점점 정교해지고 있으며, 특히 여러 클라이언트 프로젝트를 동시에 운영하는 IT 에이전시나 외주 개발사에게는 단순한 '남의 이야기'가 아니다.
왜 에이전시 환경이 특히 위험한가?
일반 기업 내부 개발팀과 달리, IT 에이전시는 구조적으로 공격 표면이 넓다.
하나의 CI/CD 파이프라인에서 복수의 클라이언트 프로젝트가 빌드된다. 개발자 한 명이 여러 프로젝트의 AWS 키, GitHub 토큰, npm 퍼블리시 권한을 동시에 보유하는 경우도 흔하다. 악성 패키지가 설치되는 순간, 그 환경에 저장된 모든 자격증명이 수집 대상이 된다.
최근 확인된 공격 방식을 보면 범위가 상당히 넓다. GitHub 토큰, AWS/Azure/GCP 클라우드 자격증명, SSH 키, .npmrc 파일, 환경변수, 그리고 각종 외부 서비스의 API 설정 파일까지 수집 대상에 포함된다. 수집된 정보는 암호화된 형태로 외부 서버로 전송되고, 탈취한 npm 토큰을 이용해 다른 패키지에 악성 코드를 재삽입하는 방식으로 감염이 확산된다.
에이전시 입장에서 이 시나리오가 무서운 이유는 하나다. 클라이언트 A의 프로젝트에서 발생한 감염이 클라이언트 B의 자격증명 탈취로 이어질 수 있다.
악성 패키지는 어떻게 탐지를 피하는가?
공격이 정교해진 데는 기술적인 이유가 있다.
난독화된 코드와 정상 패키지를 구분하기 어렵게 만드는 구조, 악성 동작을 지연 실행하는 타이밍 설계, 그리고 특정 로케일 환경에서는 아예 실행되지 않는 킬 스위치 로직까지 포함된다. 이는 단순한 바이러스 백신 스캔으로는 잡아내기 어렵다는 뜻이다.
더 주목할 점은 지속성 확보 방식이다. 셸 설정 파일(~/.bashrc, ~/.zshrc 등)에 페이로드를 삽입해 개발자가 터미널을 열 때마다 악성 코드가 실행되도록 한다. 개발 머신 자체가 지속적인 감염 상태에 놓이는 것이다. 에이전시 환경에서 개발자 로컬 머신이 공유 리소스에 접근하는 구조라면, 이 지속성은 심각한 문제가 된다.
또한 GitHub Actions 워크플로우에 무단으로 악성 스텝을 주입해 리포지토리 시크릿을 추가 수집하는 방식도 확인된다. CI 로그를 주기적으로 검토하지 않는 팀이라면 이 과정이 수 주간 발각되지 않을 수 있다.
에이전시가 즉시 실행해야 할 대응 체크리스트
보안 사고를 인지했을 때 '어디서부터 시작할지 모르겠다'는 상황을 막기 위해, 우선순위 순으로 정리한다.
1단계: 자격증명 전면 교체 의심스러운 패키지가 설치된 환경에서 사용된 모든 자격증명을 즉시 교체한다. GitHub 토큰, npm 액세스 토큰, 클라우드 IAM 키, SSH 키, CI/CD 시크릿 전부가 대상이다. 클라이언트 프로젝트에 사용된 자격증명도 포함해야 한다.
2단계: CI/CD 로그 전수 검토
최근 빌드 로그에서 비정상적인 외부 네트워크 연결, 예상치 못한 파일 접근, 알 수 없는 워크플로우 실행 이력을 확인한다. GitHub Actions 환경이라면 .github/workflows/ 디렉토리에 무단으로 추가된 파일이 없는지 살펴본다.
3단계: npm 퍼블리시 이력 감사
에이전시가 관리하는 npm 패키지가 있다면, 최근 버전 배포 이력과 preinstall 훅 변경 여부를 확인한다. 팀원이 인지하지 못한 버전이 배포되어 있다면 즉시 unpublish 후 조사에 들어간다.
4단계: 개발자 로컬 머신 점검
~/.bashrc, ~/.zshrc, ~/.profile 등 셸 설정 파일에 알 수 없는 코드가 추가되어 있는지 확인한다. 또한 임시 디렉토리(/tmp/)에 정체를 알 수 없는 락 파일이나 스크립트가 있는지도 점검한다.
5단계: 클라이언트 통보 여부 판단 클라이언트 자격증명이 포함된 환경이 감염된 경우, 해당 사실을 클라이언트에게 투명하게 알리고 함께 대응 계획을 세워야 한다. 이 단계를 미루거나 숨기는 것은 법적·계약적 리스크로 이어진다.
공급망 공격을 사전에 막는 구조 설계 원칙
사고 대응보다 중요한 건 사전 예방이다. 에이전시 차원에서 도입할 수 있는 구조적 방어책을 소개한다.
최소 권한 원칙 적용: 각 프로젝트별로 독립된 자격증명을 사용하고, 토큰의 유효 기간을 최소화한다. 한 프로젝트의 토큰이 다른 프로젝트 리소스에 접근할 수 없도록 격리한다.
패키지 버전 고정 및 무결성 검증: package-lock.json 또는 yarn.lock을 반드시 커밋하고, 패키지 해시를 검증하는 CI 단계를 추가한다. npm audit을 빌드 파이프라인에 포함시키는 것도 기본이다.
GitHub Actions 권한 최소화: 워크플로우에서 사용하는 토큰 권한을 contents: read처럼 필요한 최소 수준으로 제한한다. 외부에서 트리거되는 워크플로우(pull_request_target 등)에 특히 주의한다.
환경별 자격증명 분리: 개발, 스테이징, 프로덕션 환경의 자격증명을 철저히 분리한다. 개발 환경에서 탈취된 자격증명이 프로덕션에 영향을 미치지 않도록 하는 것이 핵심이다.
정기적인 의존성 감사: 사용 중인 패키지의 최신 보안 이슈를 추적하고, 불필요한 패키지는 주기적으로 정리한다. 자동화된 의존성 업데이트 도구를 도입하면 알려진 취약점에 대한 대응 속도를 높일 수 있다.
자주 묻는 질문
Q.우리 팀은 오픈소스 패키지를 많이 쓰는데, 공급망 공격을 완전히 막는 방법이 있나요?
완전한 차단은 현실적으로 어렵다. 하지만 리스크를 크게 줄이는 방법은 있다. 패키지 버전을 고정하고 해시를 검증하는 습관, 새 패키지를 도입할 때 최소한의 다운로드 수와 관리자 신뢰도를 확인하는 절차, 그리고 격리된 환경에서 빌드를 실행하는 구조가 기본이다. 완벽한 보안보다 '감염되더라도 피해 범위를 최소화하는 설계'에 집중하는 게 현실적인 접근이다.
Q.클라이언트 프로젝트에서 보안 사고가 발생했을 때, 에이전시는 어떤 책임을 지나요?
계약서에 보안 관련 조항이 어떻게 명시되어 있느냐에 따라 다르다. 다만 자격증명 관리 소홀이나 패키지 검증 부재가 입증되면 과실 책임으로 이어질 수 있다. 사전에 보안 정책을 문서화하고, 사고 발생 시 신속하게 클라이언트에게 통보하며 대응 기록을 남기는 것이 법적 리스크를 줄이는 핵심이다.
Q.CI/CD 환경에서 자격증명이 탈취됐는지 어떻게 확인하나요?
몇 가지 이상 징후를 확인하면 된다. 클라우드 콘솔에서 평소와 다른 시간대나 지역에서의 API 호출 이력, GitHub에서 팀원이 만들지 않은 리포지토리나 워크플로우 파일 생성 기록, npm에서 팀 내부에서 배포하지 않은 버전이 올라와 있는지 여부다. 의심 정황이 하나라도 있다면 즉시 해당 자격증명을 교체하고 역추적에 들어가는 것이 원칙이다.