ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • axios에 악성코드가 심어졌다 - 북한 해커의 npm 공급망 공격 분석
    IT 2026. 4. 7. 21:00
    axios에 악성코드가 심어졌다 - 북한 해커의 npm 공급망 공격 분석

    2026년 3월 31일, JavaScript 생태계에서 가장 많이 쓰이는 HTTP 클라이언트 라이브러리 axios가 해킹당했다. 주간 다운로드 8,300만~1억 회에 달하는 패키지에 원격 접근 트로이목마(RAT)가 심어진, 역대 최대 규모의 npm 공급망 공격이다.

    axios가 뭔데?

    axios는 JavaScript/TypeScript 진영의 사실상 표준(de facto standard) HTTP 클라이언트 라이브러리다. 브라우저의 XMLHttpRequest와 Node.js의 http 모듈을 하나의 Promise 기반 API로 추상화하여, 프론트엔드와 백엔드 어디서든 같은 코드로 HTTP 요청을 보낼 수 있게 해준다.

    왜 이렇게 많이 쓰이는 걸까?

    • 브라우저 + Node.js 겸용: 브라우저에서는 fetch의 상위 호환, Node.js에서는 node-fetch를 따로 설치할 필요 없이 하나로 통일된다
    • 인터셉터(Interceptor): 요청/응답을 가로채서 인증 토큰 자동 삽입, 에러 핸들링, 로깅 등을 미들웨어 패턴으로 처리할 수 있다. fetch에는 이런 기능이 없다
    • 자동 JSON 변환: 응답을 자동으로 JSON 파싱하고, 요청 body도 자동 직렬화한다
    • 타임아웃, 요청 취소, 진행률 추적: fetch에서 직접 구현해야 하는 기능들이 내장되어 있다
    • 생태계 관성: React, Vue, Angular 튜토리얼 대부분이 axios를 기본 HTTP 클라이언트로 사용한다. 2014년부터 쌓인 문서와 예제가 방대하다

    Python의 requests, Go의 net/http에 해당하는 위치라고 보면 된다. 주간 다운로드 8,300만~1억 회라는 숫자는 npm 전체에서도 최상위권이다. 그만큼 이 패키지 하나가 뚫리면 파급력이 어마어마하다는 뜻이기도 하다.

    무슨 일이 일어났나

    2026년 3월 31일 00:21 UTC, axios의 npm 메인테이너 계정(jasonsaayman)이 탈취되어 두 개의 악성 버전이 발행되었다:

    • axios@1.14.1 — 00:21 UTC 발행
    • axios@0.30.4 — 01:00 UTC 발행

    두 버전 모두 약 3시간 뒤인 03:29 UTC에 npm에서 제거되었다. StepSecurity가 최초 발견했고, 이후 Snyk, Huntress, Elastic Security Labs 등 다수 보안 업체가 분석 결과를 공개했다.

    공격 메커니즘: postinstall hook + 가짜 의존성

    공격 방식이 교과서적으로 정교했다.

    1. 가짜 의존성 주입: axios 코드에는 실제로 import하지 않는 plain-crypto-js@4.2.1이라는 패키지가 dependencies에 추가되었다
    2. postinstall hook 실행: npm install만 하면 이 가짜 패키지의 postinstall 스크립트가 자동 실행된다
    3. RAT 다운로드: C2 서버(sfrclak[.]com:8000)에서 플랫폼별(Windows, macOS, Linux) 원격 접근 트로이목마를 다운받아 실행한다

    npm install axios 한 줄이면 시스템이 뚫리는 구조다. CI/CD 파이프라인에서 자동으로 npm install을 실행하는 환경이라면, 사람이 인지하기도 전에 빌드 서버가 감염될 수 있다.

    왜 두 버전인가

    axios@1.14.1은 현재 최신 메이저 라인(1.x), axios@0.30.4는 레거시 라인(0.x)이다. 공격자는 두 라인 모두를 타겟했다. 최신 버전만 쓰는 프로젝트든, 레거시에 묶여있는 프로젝트든 가리지 않겠다는 전략이다.

    ^1.0.0이나 ^0.30.0 같은 semver range를 쓰는 프로젝트라면 npm install 시 자동으로 악성 버전을 받게 된다.

    배후: 북한 해킹 그룹 UNC1069

    Google Threat Intelligence Group(TIG)이 이번 공격을 UNC1069로 귀속했다. 북한 국가 지원 해킹 그룹으로, 암호화폐 탈취와 공급망 공격을 주로 수행하는 것으로 알려져 있다.

    메인테이너 계정 탈취 경로는 GitHub Actions CI/CD를 우회하여 npm에 직접 발행하는 방식이었다. GitHub 저장소의 코드에는 악성코드 흔적이 없고, npm 레지스트리에만 올라간 버전에 악성 코드가 포함되어 있었다는 점이 특징이다.

    내 서버 점검 결과

    이 소식을 접하고 바로 내 DGX Spark 서버를 전수 점검했다. 홈 디렉토리 아래 모든 프로젝트를 검사한 결과:

    프로젝트 axios 사용 버전 상태
    Google Workspace CLI (Node.js 글로벌) O 1.13.5 안전
    챗봇 게이트웨이 (Python) X (httpx) - 해당없음
    미디어 관리 도구 (Python) X (requests) - 해당없음
    RAG 검색 시스템 (Python) X - 해당없음
    음성 처리 파이프라인 (Python) X - 해당없음
    웹 대시보드 (정적 HTML) X - 해당없음
    GPU 스케줄러 (Bash) X - 해당없음
    LLM 추론 서버 (C++) X - 해당없음

    결론: 안전. axios를 사용하는 곳은 @googleworkspace/cli 하나뿐이고, 설치된 버전은 1.13.5로 악성 버전(1.14.1, 0.30.4)이 아니다. Python 프로젝트들은 대부분 requestshttpx를 사용하고 있어 이번 공격의 영향권 밖이었다.

    감염 여부 확인 방법

    본인 시스템을 점검하고 싶다면:

    # 설치된 모든 axios 버전 찾기
    find / -path "*/node_modules/axios/package.json" -exec grep '"version"' {} + 2>/dev/null
    
    # package-lock.json에서 악성 버전 검색
    grep -r "axios.*1\.14\.1\|axios.*0\.30\.4" */package-lock.json
    
    # 가짜 의존성이 설치되었는지 확인
    find / -path "*/node_modules/plain-crypto-js" 2>/dev/null

    만약 악성 버전이 설치된 흔적이 있다면, 즉시 다음을 수행해야 한다:

    • 악성 버전 제거 후 안전한 버전으로 다운그레이드 (1.14.0 또는 0.30.3)
    • npm 토큰, AWS/GCP/Azure 키, SSH 키 등 모든 크리덴셜 교체
    • .env 파일에 노출된 시크릿 값 변경
    • CI/CD 시크릿 로테이션
    • 감염 시점 이후의 시스템 로그 분석

    교훈: npm 생태계의 구조적 취약점

    이번 사건이 시사하는 점:

    • 단일 메인테이너 리스크: 주간 1억 다운로드 패키지의 npm 발행 권한이 개인 계정 하나에 묶여 있었다
    • postinstall hook의 위험성: npm install만으로 임의 코드가 실행될 수 있는 구조는 근본적으로 위험하다. --ignore-scripts 옵션을 기본값으로 두는 것을 고려해볼 만하다
    • npm과 GitHub의 분리: GitHub 코드에는 없고 npm 패키지에만 악성 코드가 있었다. 소스 코드 리뷰만으로는 잡을 수 없다
    • semver range의 양면성: ^1.0.0 같은 유연한 버전 지정은 편리하지만, 악성 패치 버전을 자동으로 끌어온다

    lockfile(package-lock.json)을 커밋하고, CI에서 npm ci를 사용하며, npm audit을 정기적으로 돌리는 기본적인 수칙이 다시 한번 강조되는 사건이다.


    이 글은 생성형 AI의 도움을 받아 작성되었습니다.

Designed by Tistory.