FEInterview Prep

Medium

최소 릴리스 기간은 저평가된 공급망 방어 수단입니다

패키지 매니저 설정 한 줄(minimumReleaseAge)로 공급망 공격의 절반 이상을 막을 수 있다. 핵심은 '먼저 들어가지 말고 남들을 먼저 보내라' 라는 시간 격리 전략이다.

2026-04-26·8분 읽기
빌드/도구Node.js
원문 보기 ↗

핵심 요약

axios 침해처럼 메인테이너 토큰 탈취 → 악성 버전 배포 → 4시간 내 제거 패턴은 레지스트리 공격의 전형이다. minimumReleaseAge 는 패키지 매니저가 '배포된 지 N일이 안 된 버전은 후보에서 제외' 하도록 만드는 설정으로, semver 범위(^, ~)로 최신을 풀어내는 일반 워크플로에서 자연스럽게 끼어든다.

주요 매니저별 설정은 같은 개념이지만 이름·단위가 제각각이다.

도구단위예시(7일)
BunminimumReleaseAge604800
pnpm (v10.16+)minimumReleaseAge10080
npm (v11.10+)min-release-age7
Yarn 4 (v4.10+)npmMinimalAgeGateduration string"7d"
RenovateminimumReleaseAgeduration"7 days"
Dependabotcooldown.default-days7

보안 업데이트는 우회가 가능해야 한다(Renovate/Dependabot 모두 CVE 패치는 cooldown 무시). 그리고 이 설정은 잠금 파일/--ignore-scripts/SHA-pinned Actions/Socket.dev 같은 동작 분석을 대체하지 않는다.

공급망 보안을 '스캐너로 잡는다' 가 아니라 '시간으로 거른다' 로 본다. 악성 패키지의 평균 노출 시간은 수 시간 단위(axios 4h, Solana 5h) 에 불과하므로, 새 버전을 N일 동안만 격리해두면 커뮤니티가 대신 검증해 준다. minimumReleaseAge 는 추가 도구가 아니라 install 시점의 해상도(resolution)에 끼어드는 지연 게이트다.

프론트엔드/Node 진영에서 공급망 공격은 더 이상 가설이 아니다. 2026년 3월 axios 침해, ua-parser-js, Solana web3.js, Ledger Connect Kit — 모두 4~5시간 안에 치고 빠지는 공격이었다. 면접에서도 이제는 다음 세 가지를 분리해 답할 수 있어야 한다.

  • 차단 가능한 공격: 단기 악성 배포 — minimumReleaseAge 7일이면 11/21 차단
  • 차단 불가능한 공격: 장기 침투(XZ Utils), 메인테이너 파괴(colors.js), 빌드 시스템 침해(SolarWinds)
  • 다른 레이어가 필요한 것: 잠금 파일, --ignore-scripts, SHA-pinned Actions, SBOM

심층 방어(defense-in-depth) 의 한 레이어로 위치를 잡을 수 있어야 한다.

학습 포인트

면접 답변으로 연결할 학습 포인트입니다.

지연(delay)이 곧 방어가 되는 통계

21건의 주요 공급망 침해를 분석한 결과 악성 버전이 레지스트리에 떠 있던 시간이 거의 항상 수 시간이었다.

  • axios: 4시간
  • Solana web3.js: 5시간
  • ua-parser-js: 4시간
  • Ledger Connect Kit: 5시간

이 패턴이 깨지지 않는 이유는 공격자도 발각 전에 끝내야 하기 때문이다. 7일 격리는 "커뮤니티의 평균 검출 시간 < 우리의 설치 시점" 을 강제로 보장한다.

exposure windowregistry takedownminimumReleaseAge심층 방어
자주 하는 오해

'몇 시간만 늦게 받자'로 충분하다고 생각하는 것. 실제 검출까지 평균 시간을 분석하면 7일 가 21건 중 11건을 막는 손익 분기점이다.

`install` 해상도에 끼어드는 게이트

이 설정은 새 버전을 후보에서 제외하는 방식이라 semver 범위로 의존성을 푸는 일반 워크플로에서 자동 작동한다.

# package.json: "axios": "^1.7.0"
# 어제 1.7.9 (악성) 가 배포되었다고 가정
npm install
# minimumReleaseAge=7 → 1.7.9 는 '나이 부족' 이라 후보 제외
# 7일 이상 된 1.7.8 로 해결됨

반면 잠금 파일을 완전히 고정해 npm ci 만 쓰면 효과가 줄어든다(다음 업데이트 시점까지 보호 무의미). 업데이트 빈도가 높을수록 가성비가 좋다.

semver rangelockfileresolutionnpm ci
자주 하는 오해

잠금 파일로 완전 고정하면 이미 안전하다 고 착각하는 것. 잠금은 이전에 본 버전 만 보호하고, 업그레이드 순간에는 동일한 위험이 다시 생긴다.

막을 수 없는 공격의 분류

21건 중 8건은 7일 게이트로도 못 막는다. 면접에서 이 한계까지 같이 말해야 깊이가 산다.

  • 장기 침투: XZ Utils(2년), event-stream(2개월) — 인내심 있는 공격
  • 메인테이너 파괴: colors.js, node-ipc — 신뢰받는 당사자가 직접 망가뜨림
  • 빌드 시스템 침해: SolarWinds, 3CX — 레지스트리 밖에서 주입
  • CDN/인프라: Polyfill.io 도메인 탈취, Codecov bash 스크립트
  • 정상 코드의 취약점: Log4Shell — 악성 배포가 아니라 버그

각 범주마다 짝이 되는 다른 레이어(Socket.dev 행위 분석, SHA-pinned Actions, SBOM, 재현 가능한 빌드)가 따로 필요하다.

long-term infiltrationbuild pipeline attackSBOMSHA-pinned Action
자주 하는 오해

minimumReleaseAge 만 켜면 공급망 보안이 끝났다고 보는 것. 이건 하나의 레이어일 뿐 SCA/잠금/CI 격리를 대체하지 않는다.

읽는 순서

  1. 1이론

    원문에 인용된 21건 중 5건(axios, ua-parser-js, Solana web3.js, Ledger Connect Kit, XZ Utils)의 침해 타임라인을 표로 정리하세요. 노출 시간공격 유형 두 컬럼만으로 7일 게이트로 잡을 수 있는 것/없는 것을 구분합니다.

  2. 2구현

    팀 레포 하나를 골라 pnpm 또는 npmminimumReleaseAge (혹은 min-release-age) 를 7로 설정하고, 의도적으로 어제 배포된 패키지를 추가해 설치 거부 로그를 캡처하세요. --ignore-min-release-age 같은 우회 플래그도 같이 검증합니다.

  3. 3실무

    Renovate(renovate.json) 또는 Dependabot(.github/dependabot.yml)에 cooldown 을 추가하되 보안 업데이트는 0일 로 우회시키세요. PR 자동화 흐름에서 cooldown 이 어떻게 적용되는지 실제 PR 한두 개로 확인합니다.

  4. 4설명

    팀 위클리에 10분짜리 발표를 합니다 — '왜 우리가 7일 게이트를 켰는가, 그리고 무엇이 여전히 우리를 못 막는가'. 막을 수 없는 5가지 카테고리와, 그것에 대응하는 다른 레이어(잠금/--ignore-scripts/SHA-pinned/SBOM)를 같이 보여줍니다.

면접 연결 질문

medium`minimumReleaseAge` 가 막을 수 있는 공격과 막을 수 없는 공격을 각각 한 사례씩 들어 설명해보세요.
힌트

[감점 답변] '대부분의 공격을 막을 수 있다'. [좋은 답변] 막을 수 있다: 2026년 axios — 메인테이너 토큰이 탈취되어 4시간 노출, 7일 게이트면 후보에서 제외됨. 막을 수 없다: XZ Utils — 메인테이너 권한을 2년에 걸쳐 빼앗은 후 정식 배포한 케이스. 시간 격리로는 정상 메인테이너의 정상 릴리스를 구별할 수 없다. 즉 단기 hit-and-run vs 장기 인사이드 위협 으로 갈린다.

hard실무에서 `minimumReleaseAge` 를 도입할 때 우회 경로(escape hatch)를 어떻게 설계하시겠습니까?
힌트

[감점 답변] '필요하면 끄면 된다'. [좋은 답변] 세 축을 분리한다.

  1. CVE 핫픽스: Renovate/Dependabot 의 보안 업데이트는 cooldown 우회 — 이미 기본 동작
  2. 내부 패키지: @org/* 스코프는 allowlist 로 제외 — 자체 빌드된 패키지에는 의미 없음
  3. 긴급 비즈니스 요구: PR 단위로 --ignore-min-release-age 같은 명시적 플래그 사용 + 코드 리뷰 의무화

핵심은 기본은 안전, 우회는 문서화된 명시 동작 으로 만드는 것.

medium잠금 파일(`package-lock.json`)을 쓰고 `npm ci` 로만 배포하는 팀에 이 기능이 의미 있을까요?
힌트

[감점 답변] '잠금 파일이 있으니 의미 없다'. [좋은 답변] npm ci 시점은 보호 무의미(이미 잠긴 버전 사용)지만, npm install / npm update / Renovate PR 시점에 끼어들어 작동한다. 따라서 의존성을 영원히 동결할 게 아니라면 의미가 있다. 오히려 잠금만 믿고 업데이트 시점 검증을 안 하는 팀이 가장 위험하다 — 업그레이드하는 순간 0일 패키지가 그대로 들어오기 때문이다.

자기 점검

axios 침해 사례에서 `minimumReleaseAge=7` 설정이 어떤 메커니즘으로 악성 버전을 걸러냈을지, 패키지 해석(resolution) 단계에서 무슨 일이 일어나는지 설명해보세요.
semver rangecandidate versionpublish timeresolution
자주 하는 오해

패키지가 다운로드된 후 검사한다고 오해하는 것. 실제로는 레지스트리 메타데이터의 publishedAt 을 보고 후보 목록에서 빼버린다.

내 팀이 사용하는 패키지 매니저(Bun/pnpm/npm/Yarn)에서 7일 지연을 설정한다면 어떤 키를 어떤 단위로 써야 합니까?
minimumReleaseAgemin-release-agenpmMinimalAgeGate단위 변환
자주 하는 오해

이름이 같으면 단위도 같다고 가정하는 것. Bun 은 초, pnpm 은 분 으로 같은 키지만 단위가 다르다.

이 기법으로 막을 수 없는 공격 카테고리 다섯 개를 나열하고, 각 카테고리에 대응하는 다른 레이어를 한 가지씩 짝지어 보세요.
장기 침투빌드 시스템CDNSocket.devSHA-pinned Action
자주 하는 오해

'시간 격리 = 공급망 보안 완성' 이라는 착각. 시간 격리는 여러 레이어 중 하나다.