공급망 공격 방어를 위한 cooldown의 효과와 한계

2026-05-17 hit count image

Dependabot cooldown과 패키지 매니저의 minimum release age 같은 시간 기반 공급망 방어 전략이 실제로 얼마나 효과적인지 지금까지 있었던 사고 데이터로 검증합니다.

web

들어가며

1편에서 axios 사례로 공급망 공격이 어떻게 작동하는지 보았고, 2편에서 실제 프로젝트에 적용한 세 가지 방어 전략을 다뤘습니다. 세 전략의 공통 원칙은 한 줄로 요약됩니다.

새로 게시된 것을 즉시 받아들이지 않는다.

이 글에서 검증하고 싶은 질문은 두 가지입니다.

  1. 이 단순한 원칙이 실제로 얼마나 효과적인가?
  2. 그리고 무엇을 막지 못하는가?

공격의 시간 패턴

공급망 공격이 시간 기반 방어로 막을 수 있다는 주장은 다음 관찰에서 출발합니다.

악성 패키지가 게시된 뒤 적발·삭제되기까지의 시간은 거의 항상 수 시간에서 수 일 안에 끝난다.

이 패턴이 성립하는 이유는 다음과 같습니다.

  • 인기 패키지일수록 자동 보안 스캐너(Socket, Snyk, GitHub Advisory 등)가 즉시 분석
  • 의심스러운 동작을 발견한 개발자가 곧바로 이슈를 제기
  • 메인테이너 본인 또는 npm 운영진이 발견 즉시 패키지를 unpublish

즉, 공격자에게는 악성 버전이 레지스트리에 살아 있는 시간 — 게시된 순간부터 적발·삭제될 때까지의 짧은 기간 — 동안에만 공격 기회가 주어집니다. 이 기간을 보안 분야에서는 노출 기간(window of exposure)이라고 부르며, 공급망 공격에서는 이 기간이 보통 매우 짧습니다. 며칠만 지나면 악성 버전은 레지스트리에서 사라져 있고, 그 사이 새 정상 버전이 게시되어 있습니다. 새 버전을 며칠 늦게 받아들이기로 결정한 사람은 이 노출 기간을 자연스럽게 비켜가게 됩니다.

알려진 사고의 노출 기간

대표적인 npm 공급망 사고들의 노출 기간을 모아 보면 다음과 같습니다.

사고시기노출 기간 (게시 → 삭제)
ua-parser-js2021약 4시간
Ledger Connect Kit2023약 5시간
Solana web3.js2024약 5시간
axios2026약 4시간

이 중 어느 것도 24시간을 넘기지 못했습니다. 즉, 단 하루만 기다려도 위 사고들의 악성 버전을 실제 프로덕트 환경에 들이지 않을 수 있었습니다.

공급망 방어에서 cooldown 기간

앞에서 본 것처럼 공급망 공격의 노출 기간은 보통 매우 짧기 때문에, “신규 버전을 며칠 늦게 받는다”는 단순한 게이트만으로 상당한 방어 효과를 얻을 수 있습니다. 그렇다면 그 게이트 기간을 며칠로 잡아야 할까요?

이 게이트 기간을 도구마다 부르는 이름이 다릅니다.

  • Dependabot: cooldown
  • pnpm · Renovate · Yarn: minimumReleaseAge (또는 npmMinimalAgeGate)
  • bun · uv: 각각 minimumReleaseAge, exclude-newer

부르는 이름은 다르지만 본질은 같습니다 — “게시된 지 N일이 지나지 않은 신규 버전은 설치·PR 생성 대상에서 제외한다.” 그리고 이 N에 대한 업계의 권장값은 거의 일관되게 7일 전후에 수렴합니다.

이 절에서는 그 7일이라는 값을 근거 → 권장 → 부작용 세 가지 관점에서 차례로 살펴봅니다.

1. 근거: 주요 도구와 보안 분석이 수렴하는 지점

7일은 임의로 고른 숫자가 아니라, 실제로 여러 패키지 매니저와 보안 연구·도구들이 비슷하게 채택·권장하고 있는 값입니다.

출처권장·기본값비고
GitHub Dependabot cooldown 공식 문서예시 설정에서 7일공식 reference의 권장 예시
pnpm minimumReleaseAge (v11)기본값 1일하위 호환을 고려한 보수적 기본값. 가이드는 더 긴 기간을 권장
pnpm 공급망 보안 가이드7일게시 후 7일 미만 버전 설치를 강하게 권장하지 않음
Renovate minimum-release-age가이드 제공공식 키 컨셉 문서로 등재
Socket7일 플래그게시 후 7일 미만인 버전을 자동으로 위험 신호로 표시

GitHub 공식 문서, 주요 패키지 매니저(pnpm·Renovate), 그리고 공급망 보안 SaaS(Socket) 모두 7일 전후를 “단명 악성 게시(short-lived malicious publish)를 거르기에 충분한 기간”으로 본다는 점이 핵심입니다.

2. 권장: 7일을 출발점으로 설정하자

종합하면 권장은 다음과 같습니다.

신규 도입 프로젝트라면 7일을 출발점으로 설정하라.

7일이 표준 권장값으로 자리 잡은 이유는 1일이나 30일과 비교했을 때 명확합니다.

  • 1일은 부족합니다. 사용자가 적은 패키지·휴일/주말 게시·메인테이너 휴가 등으로 적발이 늦어지는 경우가 있습니다. 1일은 핵심적인 4–5시간 사고들만 거를 수 있는 수준입니다.
  • 30일은 과합니다. 차단 가능 사고는 조금 더 늘어나지만, 그만큼 새 기능·기능 개선의 도입이 지연됩니다. 또한 의존성 lockfile의 신선도가 떨어져 다른 운영 비용을 만듭니다.
  • 7일은 그 사이의 합의 지점입니다. 거의 모든 단명 악성 게시를 거르면서, 정당한 새 버전을 한 주 이내로 받아들일 수 있습니다.

이를 실제 사고 데이터로 검증해 보면, 알려진 주요 공급망 사고 21건 중 7일 게이트로 차단 가능한 사고는 11건입니다. 절반 이상을 단순한 설정 한 줄로 막을 수 있다는 뜻입니다.

사고시기생태계노출 기간7일 게이트 차단
axios2026npm약 4시간차단 가능
Trivy-Action2026GitHub Actions약 12시간차단 가능
Nx / S1ngularity2025npm약 1일차단 가능
Gluestack / React Native ARIA2025npm며칠차단 가능
tj-actions/changed-files2025GitHub Actions약 3일차단 가능
reviewdog/action-setup2025GitHub Actions약 2시간차단 가능
Ultralytics YOLO2024PyPI약 1–2일차단 가능
Solana web3.js2024npm약 5시간차단 가능
Polyfill.io2024CDN약 4개월차단 불가
XZ Utils2024OS 패키지2년 이상차단 불가
Ledger Connect Kit2023npm약 5시간차단 가능
3CX2023데스크톱 앱수 주차단 불가
PyTorch torchtriton2022PyPI약 5일차단 가능
node-ipc2022npm수 주 이상차단 불가
colors / faker2022npm수일~수 주부분 차단
Log4Shell2021Java해당 없음차단 불가
ua-parser-js2021npm약 4시간차단 가능
Codecov2021SaaS약 2개월차단 불가
Dependency Confusion (Alex Birsan)2021복수사례별 상이부분 차단
SolarWinds2020빌드 시스템약 9개월차단 불가
event-stream2018npm약 2개월차단 불가

부분 차단은 공격의 일부 단계만 막을 수 있는 경우(예: 첫 며칠의 노출은 막지만 이후 단계는 통과)를 의미합니다. 단순 집계로는 차단 가능 11건 / 차단 불가 8건 / 부분 차단 2건입니다.

표에서 한 가지 더 읽을 수 있는 사실은, 차단 불가 사고들은 모두 7일 게이트의 본질적 한계와 직결되어 있다는 점입니다. XZ Utils·SolarWinds·event-stream처럼 노출 기간이 수개월에서 수년에 이르는 장기 침투, Polyfill.io·Codecov처럼 npm 레지스트리 외부에서 일어난 공격, Log4Shell처럼 정상 코드의 결함은 모두 cooldown으로는 막을 수 없는 영역입니다. 이 한계들은 뒤의 어떤 공격은 막을 수 없는가에서 자세히 다룹니다.

물론 14일·30일로 늘리면 차단 가능 사고가 조금 더 늘어나지만, 그만큼 정당한 보안 패치를 받는 것도 늦어진다는 비용이 따라옵니다. 7일은 그 균형이 비교적 잘 맞는 지점이라는 것이 현재의 업계 합의에 가깝습니다.

어떤 공격을 막을 수 있는가

7일 게이트는 다음과 같은 유형에 효과적입니다.

1. 메인테이너 계정 탈취형

가장 흔하고, 가장 빠르게 적발되는 유형입니다. 1편axios 사례가 여기에 해당합니다. 탈취 → 악성 게시 → 적발 → 삭제까지의 사이클이 보통 24시간 이내에 끝나기 때문에, 7일 게이트로 거의 모두 차단됩니다.

2. 타이포스쿼팅 (typosquatting)

react-doom처럼 인기 패키지와 유사한 이름의 가짜 패키지를 만들어 오타로 설치되기를 노리는 공격입니다. 이런 패키지는 게시 직후 자동 스캐너에 잡혀 며칠 안에 내려갑니다.

3. 의존성 컨퓨전 (dependency confusion)

내부 사설 패키지와 같은 이름의 패키지를 공개 레지스트리에 올려, 빌드 시스템이 잘못 받아가도록 유도하는 공격입니다. 이 역시 발견 즉시 삭제되는 패턴을 따릅니다.

4. 빠른 의존성 주입형

axios 사례의 plain-crypto-js처럼, 침해된 패키지가 신규 악성 패키지를 새 의존성으로 추가하는 형태입니다. 핵심은 그 신규 의존성 자체가 갓 게시된 신규 패키지라는 점입니다. 7일 게이트는 침해된 메인 패키지의 새 버전도, 그것이 끌고 들어오는 신규 의존성도 모두 차단합니다.

어떤 공격은 막을 수 없는가

방어 전략을 평가할 때 더 중요한 질문은 “이게 막지 못하는 건 무엇인가”입니다. 7일 게이트가 무력한 경우는 다음과 같습니다.

1. 장기 침투형 — XZ Utils 사례

2024년에 발견된 XZ Utils 백도어는 공격자가 2년에 걸쳐 메인테이너로서의 신뢰를 쌓은 뒤 백도어를 삽입한 사례입니다. 정상적인 정식 릴리즈를 통해 백도어가 들어왔고, 그 코드는 수개월 동안 누구의 의심도 받지 않았습니다.

7일 게이트는 “갓 게시된 것”을 막지만, 게시 후 수개월이 지나 정상으로 인정받은 버전에 대해서는 무력합니다.

2. 메인테이너 본인의 의도적 사보타주

colors.js(2022)와 node-ipc(2022) 사례처럼, 메인테이너 본인이 의도적으로 악성 코드를 삽입하는 경우가 있습니다. 이 경우에도 코드는 정식 릴리즈 절차를 거쳐 게시됩니다. 7일 게이트는 처음 며칠은 막아주지만, 메인테이너가 의도적으로 천천히 진행한다면 게이트를 통과한 뒤 악성 코드가 퍼집니다.

3. 빌드 시스템 침해

SolarWinds(2020), 3CX(2023) 같은 사고는 패키지 레지스트리가 아니라 빌드 시스템 자체가 침해된 경우입니다. 정상적인 버전 번호로 정상 절차를 통해 배포되었지만, 빌드 산출물 자체에 백도어가 포함되어 있었습니다. 패키지 매니저 레벨의 방어는 여기에 닿지 않습니다.

4. 레지스트리 외부 공격

Codecov(2021)는 CI 환경에서 다운로드되는 셸 스크립트가 변조된 사고였고, Polyfill.io(2024)는 CDN을 통해 직접 JavaScript를 로드하다가 도메인이 인수되면서 악성 코드가 주입된 사고였습니다. 둘 다 npm 게이트와 무관한 경로로 들어옵니다.

5. 합법적인 코드 안의 정상적인 취약점

Log4Shell처럼, 악의 없이 작성된 정상 라이브러리에 보안 결함이 있는 경우입니다. 이건 공급망 공격이 아니라 일반적인 취약점이고, 오히려 빨리 패치를 받는 것이 정답입니다. 7일 게이트는 이 경우 오히려 단점이 됩니다.

공격 유형7일 게이트 효과
메인테이너 계정 탈취매우 효과적
타이포스쿼팅매우 효과적
의존성 컨퓨전매우 효과적
빠른 의존성 주입매우 효과적
장기 침투 (XZ Utils형)무력
메인테이너 사보타주제한적
빌드 시스템 침해무력
레지스트리 외부 (CDN, 스크립트)무력
정상 라이브러리의 취약점오히려 단점

방어의 비용

이 방어가 “단순하고 효과적이다”라고 평가받는 이유는 비용이 거의 들지 않기 때문입니다.

비용 항목수준
설정 변경량설정 파일 몇 줄
학습 곡선거의 없음
CI/CD 파이프라인 변경Yarn 4 업그레이드 시 --immutable 치환 정도
정당한 패치 수신 지연평균 7일 (보안 알림은 즉시)
운영 부담거의 없음 (Dependabot이 자동 처리)

가장 큰 비용은 “정당한 새 버전을 7일 늦게 받는 것”인데, 이것조차 보안 알림은 cooldown 대상이 아니기 때문에 실제로 큰 문제는 잘 발생하지 않습니다.

defense-in-depth의 한 수단으로

핵심은 이 전략이 만능이 아니며, 그래서도 안 된다는 것입니다. 7일 게이트는 빠른 적발/삭제 패턴에 맞춰 만든 시간 기반 필터일 뿐입니다. 위에서 본 것처럼 장기 침투, 빌드 시스템 침해, 레지스트리 외부 공격은 다른 방어 수단이 필요합니다.

여기서 말하는 defense-in-depth(심층 방어)란, 하나의 강력한 방어에만 의존하지 않고 성격이 다른 여러 방어 수단을 겹쳐 운영하는 보안 원칙입니다. 한 수단이 막지 못한 공격을 다음 수단이 막아 주는 식으로, 서로 약점을 보완하도록 배치합니다. cooldown은 그 여러 수단 중 하나에 불과합니다.

cooldown과 함께 운영하면 좋은 다른 방어 수단들은 다음과 같습니다.

방어 수단막는 공격
enableScripts: falsepostinstall로 실행되는 즉시 RCE
GitHub Actions SHA 핀닝Actions 자체의 변조
Hardened Mode (Yarn 4)lockfile 변조
OIDC / signed publish비공식 경로의 게시
SBOM 관리의존성 가시성
자동 보안 스캐너알려진 취약점·악성 패턴 탐지
코드 리뷰의도적 사보타주 (의존성 변경에 대한 주의)
CDN 무결성 (SRI)레지스트리 외부 자산 변조

2편에서 다룬 세 전략은 이 표의 일부에 해당합니다. 나머지는 조직의 상황에 맞게 단계적으로 도입할 수 있습니다.

마무리

이 시리즈를 정리하면 다음과 같습니다.

  • 1편: npm 신뢰 모델은 구조적으로 공격에 노출되어 있고, axios 사례는 악성 버전이 살아 있는 노출 기간이 매우 짧지만 분명히 실재한다는 것을 보여줬다.
  • 2편: 실제 프로젝트 환경에서는 GitHub Actions SHA 핀닝, Dependabot cooldown, Yarn npmMinimalAgeGate 세 가지로 그 노출 기간을 자연스럽게 비켜갈 수 있다.
  • 3편(이 글): 단순한 시간 기반 게이트는 알려진 공급망 사고의 상당수를 막을 수 있지만, 만능이 아니므로 다른 방어 수단과 함께 운영해야 한다.

공급망 보안은 본질적으로 확실한 한 방이 없는 영역입니다. 모든 의존성을 직접 감사할 수도 없고, 모든 메인테이너의 의도를 검증할 수도 없습니다. 그래서 더더욱 “비용이 거의 들지 않는 단순한 방어”가 빛을 발합니다. 7일이라는 숫자 하나에 설정 파일 몇 줄, 그것만으로 알려진 공급망 공격의 절반 이상을 막을 수 있다는 점은 — 들인 노력에 비해 얻는 효과가 이만큼 큰 방어라는 점에서 — 보안 영역에서 좀처럼 보기 드문 경우입니다.

여러분의 프로젝트에 아직 cooldown이나 minimum age gate가 없다면, 지금이 도입할 가장 좋은 시기입니다.

참고 자료

제 블로그가 도움이 되셨나요? 하단의 댓글을 달아주시면 저에게 큰 힘이 됩니다!

앱 홍보

책 홍보

블로그를 운영하면서 좋은 기회가 생겨 책을 출판하게 되었습니다.

아래 링크를 통해 제가 쓴 책을 구매하실 수 있습니다.
많은 분들에게 도움이 되면 좋겠네요.



SHARE
Twitter Facebook RSS