3월 31일 axios 해킹 사건 정리: 프론트엔드 개발자가 바로 할 수 있는 보안 대책

Frontend

2026년 3월 31일, 프론트엔드 생태계에서 매우 상징적인 사건이 하나 터졌습니다. 매우 널리 쓰이는 axios 패키지의 npm 배포 계정이 탈취되면서 악성 버전이 실제 레지스트리에 올라갔고, 그 짧은 시간 안에 설치한 개발 환경과 CI 환경이 감염될 수 있는 상황이 발생했습니다.

이 사건이 무서운 이유는 단순히 "axios가 해킹당했다"가 아닙니다.

  • 많은 팀이 너무 익숙한 패키지를 별 의심 없이 설치하고
  • CI가 자동으로 새 버전을 받아오며
  • postinstall 같은 lifecycle script가 생각보다 강한 권한으로 실행되고
  • 개발자 노트북과 빌드 러너 안에는 각종 토큰과 키가 이미 들어 있기 때문입니다

즉, 이 사건은 특정 라이브러리 하나의 문제가 아니라 프론트엔드 개발 환경 자체가 이미 공격 표면이라는 사실을 다시 보여준 사례에 가깝습니다.

이 글에서는 아래 흐름으로 정리해보겠습니다.

  1. 3월 31일 axios 사건에서 실제로 무슨 일이 있었는지
  2. 왜 프론트엔드 개발자도 이 문제를 심각하게 봐야 하는지
  3. 오늘 바로 적용할 수 있는 보안 대책은 무엇인지

먼저 한눈에 보면

짧게 요약하면 이렇습니다.

  • 공격자는 axios 메인테이너 계정을 탈취해 악성 버전을 npm에 배포했습니다
  • 악성 버전은 plain-crypto-js@4.2.1 의존성을 추가해 postinstall 시점에 악성 코드를 실행했습니다
  • 영향 범위는 브라우저 런타임보다 설치 시점의 개발자 PC, CI/CD, 빌드 환경이었습니다
  • 따라서 대응의 핵심은 "앱 코드만 안전하면 된다"가 아니라 의존성 설치 경로와 비밀 관리 경로를 줄이는 것입니다

즉, 이 사건은 프론트엔드 보안이 XSS, CSRF, 인증 우회 같은 런타임 문제만이 아니라, 패키지 설치와 배포 체인까지 포함하는 문제라는 점을 아주 강하게 보여줬습니다.

3월 31일 axios 사건에서 무슨 일이 있었나

공개된 보안 리포트들을 기준으로 보면, 공격자는 axios 메인테이너의 npm 계정에 접근한 뒤 악성 버전을 공식 npm 레지스트리에 게시했습니다.

특히 문제가 된 버전은 아래 두 개였습니다.

  • axios@1.14.1
  • axios@0.30.4

이 버전들에는 원래 axios 소스 코드에 직접 드러나는 백도어가 들어간 것이 아니라, plain-crypto-js@4.2.1이라는 악성 의존성이 새로 추가되었습니다.

문제는 여기서 끝이 아닙니다. 이 악성 의존성은 설치 과정에서 postinstall 스크립트를 실행해 추가 드로퍼를 내려받고, 운영체제별로 원격 제어용 페이로드를 받아 실행하도록 설계돼 있었습니다.

정리하면 흐름은 대략 이렇습니다.

npm install
  -> axios 악성 버전 해석
  -> plain-crypto-js 설치
  -> postinstall 실행
  -> 추가 스크립트/페이로드 다운로드
  -> OS별 RAT 실행 및 비밀정보 탈취 가능

즉, 사용자가 브라우저에서 우리 서비스를 열었다고 바로 감염되는 종류의 문제는 아니었습니다. 더 정확히 말하면, 개발자나 자동화 시스템이 설치를 수행하는 순간이 위험한 문제였습니다.

왜 프론트엔드 개발자가 특히 신경 써야 할까

겉으로만 보면 이런 생각을 하기 쉽습니다.

  • "axios는 그냥 HTTP 클라이언트 아닌가?"
  • "우리 앱은 브라우저에서만 도니까 서버 침해랑은 좀 다른 것 아닌가?"

하지만 실제로 프론트엔드 팀이 이 사건에 민감해야 하는 이유는 분명합니다.

1. 프론트엔드 프로젝트도 설치 시점에는 로컬 권한을 가진다

프론트엔드 애플리케이션이 최종적으로는 브라우저에서 실행되더라도, npm install, pnpm install, yarn install은 개발자 노트북이나 CI 러너에서 실행됩니다.

그 환경 안에는 종종 이런 값들이 있습니다.

  • .env에 들어 있는 API 키
  • Vercel, AWS, Cloudflare 같은 배포 토큰
  • GitHub PAT, npm token
  • SSH 키, 클라우드 자격 증명
  • 사내 패키지 레지스트리 접근 토큰

즉, 프론트엔드 저장소라고 해서 공격 가치가 낮지 않습니다. 오히려 배포 자동화와 토큰이 많이 엮여 있어서 연쇄 침해의 출발점이 되기 쉽습니다.

2. CI/CD는 사람이 생각하는 것보다 더 많은 비밀을 갖고 있다

실무에서는 PR 검증, preview 배포, static export, 스토리북 배포, E2E 테스트 같은 이유로 프론트엔드 CI가 꽤 강한 권한을 들고 있는 경우가 많습니다.

예를 들면:

  • package registry publish 권한
  • preview 환경 배포 권한
  • 모니터링 서비스 업로드 토큰
  • source map 업로드 토큰
  • 디자인 시스템 배포 토큰

이 상태에서 악성 패키지가 설치되면, 단순히 로컬 머신 한 대가 아니라 공급망 다음 단계로 이어지는 권한이 노출될 수 있습니다.

3. 익숙한 패키지일수록 경계가 풀리기 쉽다

axios처럼 오랫동안 써온 유명 패키지는 많은 팀에서 거의 인프라처럼 받아들여집니다. 그래서:

  • changelog를 자세히 안 보고
  • patch 버전은 거의 자동 수용하고
  • lockfile 변경을 깊게 리뷰하지 않으며
  • install script까지는 잘 신경 쓰지 않는 경우가 많습니다

공격자는 바로 이 신뢰를 노립니다. 보안 관점에서 보면, 가장 위험한 패키지는 "생소한 패키지"만이 아니라 너무 익숙해서 의심을 덜 받는 패키지일 수도 있습니다.

이 사건에서 핵심적으로 봐야 할 포인트

이번 사건을 프론트엔드 실무 관점으로 번역하면 아래 네 가지가 핵심입니다.

1. 런타임 공격보다 설치 체인 공격이었다

브라우저에 배포된 번들 자체가 자동으로 악성 동작을 하는 문제라기보다, 의존성을 설치하는 순간 악성 코드가 실행되는 문제였습니다.

그래서 질문도 달라져야 합니다.

  • "우리 앱이 취약한가?"보다
  • "우리 설치 경로가 안전한가?"

를 먼저 봐야 합니다.

2. postinstall은 생각보다 강력하다

많은 개발자가 lifecycle script를 "설치 편의용 스크립트" 정도로 생각하지만, 실제로는 로컬 머신과 CI에서 꽤 강한 권한으로 실행됩니다.

예를 들어 이런 형태입니다.

{
  "scripts": {
    "postinstall": "node setup.js"
  }
}

이 한 줄이 네트워크 통신, 파일 생성, 프로세스 실행, 환경 변수 읽기까지 이어질 수 있습니다.

즉, package manager는 단순한 다운로드 도구가 아니라, 어떤 의미에서는 코드를 실행하는 플랫폼이기도 합니다.

3. lockfile과 버전 고정은 성능 문제가 아니라 보안 문제이기도 하다

우리는 보통 lockfile을 재현성이나 CI 안정성 관점에서 많이 봅니다. 하지만 이번 사건은 lockfile이 보안 완충 장치이기도 하다는 점을 보여줬습니다.

만약 팀이 이미 안전한 버전을 lockfile에 고정해두고 immutable 설치를 사용하고 있었다면, 짧은 공격 노출 시간 동안 자동으로 악성 버전을 받아올 가능성을 줄일 수 있었습니다.

즉:

  • package-lock.json
  • pnpm-lock.yaml
  • yarn.lock

은 단순한 설치 편의 파일이 아니라, 무심코 최신 버전을 받아오는 일을 줄여주는 방어선이기도 합니다.

4. 개발 환경은 이미 프로덕션급 자산이다

예전에는 "개발자 로컬은 좀 덜 중요하고, 프로덕션 서버만 잘 지키면 된다"는 식의 감각이 남아 있는 경우가 있었습니다. 하지만 지금은 다릅니다.

개발자 노트북과 CI에는:

  • 코드 서명 관련 정보
  • 배포 권한
  • 레지스트리 접근 권한
  • 내부 서비스 접근 키

가 모여 있습니다.

그래서 공급망 공격에서는 개발 환경이 곧 고가치 타깃입니다.

프론트엔드 개발자가 바로 할 수 있는 보안 대책

여기서부터가 실무적으로 더 중요합니다. 모든 팀이 보안 전담 조직을 크게 두고 있지는 않기 때문에, 프론트엔드 팀이 오늘부터 바로 적용할 수 있는 수준으로 정리해보겠습니다.

1. lockfile을 반드시 커밋하고, CI에서는 immutable 설치를 강제하기

가장 기본이지만 효과가 큽니다.

예시:

# npm
npm ci
 
# pnpm
pnpm install --frozen-lockfile
 
# Yarn Berry
yarn install --immutable

이렇게 해두면 CI가 그날그날 새로 해석한 버전을 받아오기보다, 리뷰된 lockfile 기준으로 설치하게 됩니다.

특히 중요한 점은:

  • 로컬 개발은 느슨해도
  • CI와 배포 파이프라인은 반드시 고정되게 만드는 것입니다

즉, "자동 최신화"보다 리뷰된 변경만 반영한다는 쪽으로 설치 정책을 바꾸는 것이 첫 번째 방어선입니다.

2. semver 범위를 너무 느슨하게 두지 않기

아래처럼 광범위한 범위는 편해 보이지만, 예상하지 못한 설치 결과를 부를 수 있습니다.

{
  "dependencies": {
    "axios": "^1.14.0"
  }
}

물론 semver 자체가 나쁜 것은 아닙니다. 다만 보안 관점에서 보면:

  • 누가 언제 새 버전을 publish하는지
  • 그 버전이 실제 GitHub 태그와 대응되는지
  • CI가 그 순간 새 버전을 바로 가져오는지

를 같이 봐야 합니다.

현실적으로는:

  • 애플리케이션은 lockfile 기반으로 고정하고
  • 의존성 업데이트는 Renovate 같은 도구로 PR 단위로 받으며
  • 자동 merge 범위는 보수적으로 가져가는 편이 안전합니다

즉, 업데이트를 막자는 뜻이 아니라 업데이트를 관찰 가능한 이벤트로 바꾸자는 뜻입니다.

3. postinstall과 lifecycle script를 무조건 신뢰하지 않기

실무에서 아주 중요한 포인트입니다.

패키지를 추가할 때 아래를 한 번은 확인하는 습관이 좋습니다.

  • 새 dependency가 생겼는가
  • 갑자기 install script가 추가됐는가
  • 이전엔 없던 binary download가 생겼는가
  • package.json 변경에 비해 lockfile diff가 이상하게 큰가

예를 들어 lockfile이나 package.json 리뷰에서 이런 변화는 더 주의해서 봐야 합니다.

{
  "scripts": {
    "install": "node install.js",
    "postinstall": "node setup.js"
  }
}

모든 install script가 악성이라는 뜻은 아닙니다. esbuild, sharp, puppeteer처럼 정상적인 설치 단계가 필요한 패키지도 많습니다. 중요한 것은 "설치 스크립트가 있으면 원래 그런가 보다"로 넘기지 않는 것입니다.

필요하다면 CI 일부 구간에서 --ignore-scripts를 검토할 수 있지만, 이 옵션은 정상 패키지 동작까지 깨뜨릴 수 있으므로 팀 환경에 맞게 선별적으로 써야 합니다.

4. CI 비밀을 최소화하고, job 단위로 권한을 쪼개기

이건 프론트엔드 팀이 생각보다 바로 개선할 수 있는 부분입니다.

예를 들면:

  • 테스트 job에는 배포 토큰을 넣지 않기
  • preview 배포 job과 production 배포 job의 권한 분리하기
  • source map 업로드 토큰을 모든 job에 공통 주입하지 않기
  • 읽기만 필요한 작업에 쓰기 권한 토큰을 넣지 않기

즉, 핵심은 "설치가 일어나는 모든 job이 중요한 비밀을 다 가지고 있지 않게 만들기" 입니다.

공급망 공격은 종종 "코드 실행"보다 "비밀 노출"이 더 큰 피해로 이어집니다. 그래서 secret minimization은 매우 실용적인 방어책입니다.

5. 개발자 로컬 환경도 보안 범위에 넣기

이번 사건은 로컬 개발 머신도 침해 경로가 될 수 있다는 점을 잘 보여줬습니다.

프론트엔드 팀 입장에서 현실적으로 해볼 수 있는 것은:

  • 장기 토큰보다 만료가 짧은 토큰 사용
  • .env에 모든 키를 상시 저장하지 않기
  • 개인 npm token, GitHub PAT 권한 최소화
  • 로컬에 저장된 클라우드 자격 증명 정리
  • 보안 민감 작업은 가능한 한 별도 계정 또는 별도 프로필 사용

완벽하게 막을 수는 없더라도, 한 번 뚫렸을 때 공격자가 바로 가져갈 수 있는 것을 줄여야 합니다.

6. 의존성 업데이트를 "보안 이벤트"처럼 다루기

유명 라이브러리의 patch 업데이트라고 해서 항상 가볍게 보면 안 됩니다.

특히 아래 조건이면 더 주의할 만합니다.

  • 다운로드 수가 매우 큰 패키지
  • 팀 전체가 넓게 쓰는 패키지
  • CI와 로컬에서 자동 설치되는 패키지
  • 네이티브 바이너리나 install script를 동반하는 패키지

현실적인 운영 방식은 이렇습니다.

  • 업데이트 PR을 자동 생성하되 자동 병합은 제한
  • changelog, GitHub release, publish 이력 간 불일치 확인
  • lockfile diff를 코드 diff만큼 중요하게 보기
  • "왜 이 dependency가 추가됐는가"를 PR 리뷰 질문에 포함

즉, dependency update는 단순 유지보수 작업이 아니라 공급망 변경 관리입니다.

7. incident checklist를 팀 문서로 남기기

사건이 터졌을 때 매번 처음부터 생각하면 대응이 느려집니다. 최소한 아래 정도는 팀 위키나 저장소 문서로 정리해두는 것이 좋습니다.

예시:

  1. 영향을 받은 버전 확인
  2. lockfile / 캐시 / CI 로그 확인
  3. 해당 버전을 설치한 머신 목록 추적
  4. 관련 토큰 회전
  5. 배포 시스템 접근 로그 확인
  6. 필요하면 러너 및 개발 환경 재구성

이 체크리스트가 있으면, 실제 사고 시 "누가 무엇부터 볼지"가 훨씬 빨라집니다.

만약 우리 팀이 영향을 받았을 수 있다면 무엇부터 해야 할까

이번 사건처럼 실제 악성 설치가 의심될 때는 단순히 node_modules만 지우고 끝내면 안 됩니다.

우선순위는 보통 이렇게 잡는 편이 맞습니다.

  1. 영향을 받은 버전 설치 여부 확인
  2. 설치된 머신과 CI runner 식별
  3. 해당 환경의 비밀정보 회전
  4. 의심 머신 격리 및 재구성 검토
  5. 배포·레지스트리·클라우드 접근 로그 점검

특히 중요한 점은, 한 번 악성 postinstall이 실행된 환경은 "패키지만 되돌리면 끝"이라고 보기 어렵다는 것입니다.

예를 들어 아래 항목이 확인되면 더 강하게 대응해야 합니다.

  • axios@1.14.1, axios@0.30.4, plain-crypto-js@4.2.1 설치 흔적
  • Windows의 %PROGRAMDATA%\wt.exe
  • Linux의 /tmp/ld.py
  • macOS의 /Library/Caches/com.apple.act.mond

즉, 패키지 버전 문제로만 보지 말고 환경 침해 가능성으로 봐야 합니다.

이 사건이 우리에게 남긴 교훈

이번 사건은 "axios가 위험하다"는 식으로 받아들이면 오히려 핵심을 놓치기 쉽습니다.

핵심은 아래에 더 가깝습니다.

  • 인기 패키지도 공급망 공격 대상이 될 수 있고
  • patch 버전도 자동 신뢰하면 안 되며
  • 프론트엔드 CI와 개발자 노트북은 이미 고가치 자산이고
  • lockfile, 권한 분리, script 검토 같은 기본기가 실제 방어선이 된다는 점입니다

결국 프론트엔드 보안은 브라우저 안에서 끝나지 않습니다. 우리가 매일 실행하는 install, build, deploy가 모두 보안 경계 안에 들어와 있습니다.

이 사건을 계기로 팀 차원에서 꼭 다시 봐야 할 질문은 이것입니다.

  • 우리 CI는 리뷰된 lockfile만 설치하는가
  • dependency update는 얼마나 관찰 가능한가
  • install script가 실행되는 환경에 어떤 비밀이 들어 있는가
  • 프론트엔드 개발자 로컬 환경도 침해 대상으로 보고 있는가

이 질문에 답할 수 있으면, 다음 공급망 공격이 와도 훨씬 덜 당황하게 됩니다.

프론트엔드 팀용 실무 체크리스트

마지막으로 아주 짧게 정리하면, 프론트엔드 팀은 아래부터 점검하면 됩니다.

  1. CI에서 npm ci, pnpm install --frozen-lockfile, yarn install --immutable 중 하나를 강제하고 있는가
  2. lockfile이 항상 커밋되고 리뷰되는가
  3. dependency 업데이트가 PR 단위로 추적되는가
  4. install script가 추가될 때 리뷰 포인트가 존재하는가
  5. 테스트 job과 배포 job의 비밀 권한이 분리돼 있는가
  6. 로컬 개발 환경에 장기 토큰을 너무 많이 저장하고 있지 않은가
  7. 공급망 사고 시 secret rotation과 러너 재구성 절차가 있는가

이런 기본기가 쌓이면, 거대한 보안 솔루션이 없어도 공급망 공격에 대한 내성이 확실히 올라갑니다.

같이 보면 좋은 글