모두가 의존성 쿨다운을 사용해야 하는 이유
(blog.yossarian.net)- 의존성 쿨다운(dependency cooldown) 은 오픈소스 공급망 공격의 대부분을 완화할 수 있는 간단하고 효과적인 보안 기법
- 공격자는 보통 인기 오픈소스 프로젝트를 탈취해 악성 코드를 배포하지만, 대부분의 공격 노출 기간이 일주일 이하로 짧음
- 새 버전 공개 후 일정 기간(예: 7일) 대기하는 쿨다운을 설정하면, 자동 업데이트로 인한 감염 위험을 크게 줄일 수 있음
- Dependabot, Renovate, pnpm 등은 이미 쿨다운 기능을 기본 지원하며, 설정이 쉽고 추가 비용이 없음
- 패키지 관리자 수준에서 쿨다운을 기본 제공하면, 공급망 보안 강화와 불필요한 경보 감소에 기여 가능
공급망 공격의 구조와 문제점
- 대부분의 공급망 공격(supply chain attack) 은 동일한 패턴을 가짐
- 공격자가 인기 오픈소스 프로젝트의 자격 증명 탈취 또는 CI/CD 취약점을 이용해 접근
- 악성 변경을 배포 채널(PyPI, npm 등)에 업로드
- 자동 업데이트나 버전 고정 미비로 인해 사용자가 감염된 버전을 설치
- 보안 벤더가 이를 탐지해 경고 후, 패키지 저장소가 해당 버전을 제거
- (1)~(2)단계 간격은 길지만, (2)~(5)단계는 수 시간~수일 내 처리되어 공격자의 활동 기간이 짧음
- 최근 18개월간 주요 사례의 공격 가능 기간(window of opportunity)
- xz-utils: 약 5주
- Ultralytics: 12시간(1단계), 1시간(2단계)
- tj-actions: 3일
- chalk: 12시간 미만
- Nx: 4시간
- rspack: 1시간
- num2words: 12시간 미만
- Kong Ingress Controller: 약 10일
- web3.js: 5시간
- 이 중 8건이 1주 미만의 공격 기간을 가졌으며, 대부분 쿨다운으로 차단 가능
쿨다운의 개념과 효과
-
쿨다운(cooldown) 은 새 의존성이 공개된 후 일정 기간 동안 사용을 지연하는 방식
- 이 기간 동안 보안 벤더가 악성 여부를 탐지할 수 있음
- 장점
- 실증적으로 효과적이며, 대규모 공격의 대부분을 차단
- 구현이 매우 간단하고 대부분의 도구에서 무료로 설정 가능
- Dependabot 예시
version: 2 - package-ecosystem: github-actions directory: / schedule: interval: weekly cooldown: default-days: 7 - 보안 벤더의 긍정적 행동 유도: 과도한 경보나 홍보 대신 신속한 탐지에 집중하게 함
결론 및 제언
- 10건 중 8건의 공격이 1주 이하의 기간이었으며, 7일 쿨다운으로 대부분 차단 가능
- 14일 쿨다운을 적용하면 xz-utils를 제외한 모든 사례 방어 가능
- 쿨다운은 완벽한 해결책은 아니지만, 노출 위험을 80~90% 줄이는 간단한 방법
- Dependabot, Renovate 외에도 패키지 관리자 자체에서 쿨다운을 기본 지원하도록 개선 필요
- 공급망 보안은 기술적 문제뿐 아니라 사회적 신뢰 구조의 문제이지만, 쿨다운은 현실적 완화책으로 유용함
최근에는 그냥 바퀴를 재발명하는 노력과 의존성 테트리스 관리하는 노력 중 어느 게 더 감당할만한지 헷갈릴 때가 있어요.
If for while 잘못된 건 그냥 내 코드 고치면 되는데, 의존성 테트리스 중 어느 바퀴가 갑자기 틀어진 건 디버깅도 어려워서요.
사실 문제가 없으면 굳이 업데이트를 안 하는게 더 나을것 같아요.
이전 버전과 크게 다를게 없는 새 버전을 위험성을 감수하면서 꼭 적용해야 할까요.
Hacker News 의견
-
사람들은 즉시 업데이트하지 않으면 심각한 취약성에 노출된다고 걱정하지만, 실제로는 대부분 그렇지 않음
많은 소프트웨어는 지속적 배포가 아니라 고객이 직접 새 버전을 설치하는 방식이라 몇 주나 몇 달 주기로 업데이트됨
중요한 것은 의존성 모니터링과 공개된 취약점 확인임. 제품이 실제로 영향을 받는지 평가한 뒤, 그때만 해당 의존성을 즉시 업데이트하면 됨- 생태계 전반에서 이런 선별적 업데이트 문화가 부족함
새 버전이 나오면 무조건 오늘 업데이트해야 한다는 인식이 퍼져 있음
실제 변경 사항을 검토하지 않고, “나중에 더 힘들어질 테니 지금 하자” 식의 접근은 비효율적임
버전 번호의 최전선에 머무는 것이 보안상 역효과일 수도 있음 - 현실적인 문제는 많은 대기업의 보안팀이 “zero CVE” 정책을 강제한다는 점임
우리 회사는 스캐너가 치명적 취약점을 발견하면 7일 내 업데이트해야 함
기한을 넘기면 규정 위반으로 복잡한 절차가 시작되므로, 대부분은 그냥 모든 걸 즉시 업데이트함 - 사람들은 0-day를 두려워하지만, 실제 문제의 대부분은 수백 일 동안 패치되지 않은 취약점에서 발생함
- 핵심은 앱이 외부에서 오는 알 수 없는 입력을 받는지 여부임
브라우저처럼 외부 입력이 많은 앱은 자주 업데이트해야 하지만, 날씨 앱처럼 입력이 제한된 경우는 상대적으로 안전함 - “필요할 때만 업데이트” 전략은 이론상 좋아 보이지만, 실제로는 각 취약점을 제품에 맞춰 평가하는 비용이 너무 큼
차라리 정기적으로 업데이트하고 공급망 공격 방어 조치를 병행하는 게 효율적임
- 생태계 전반에서 이런 선별적 업데이트 문화가 부족함
-
Debian stable 모델처럼 배포판이 공통 의존성을 관리하고 몇 년마다 전체 업그레이드를 하는 방식이 점점 더 합리적으로 보임
일부 생태계는 너무 빠르게 움직이거나 배포판별 패키징 체계가 부실함
예를 들어 Node.js 라이브러리를 apt로 설치해 프로젝트에서 쓰는 건 여전히 어려움- “움직임”을 “행동”으로 착각하지 말아야 함
근본적 변화 없이 빠르게 움직이는 생태계는 건강하지 않음
JS는 지난 3년간 실질적 진보가 거의 없는데, 3년 된 프로젝트를 다시 빌드하려면 리라이트 수준의 고통이 따름 -
Debian node 패키지 검색 결과를 보면 일부 존재하지만 완전하지 않음
Arch 같은 배포판은 아예 없는 경우도 있음 - Rust는 Debian 저장소만으로도 충분히 작업 가능함
- 안정 버전 모델은 앱이 구버전과 신버전을 동시에 지원해야 하는 부담이 있음
- “움직임”을 “행동”으로 착각하지 말아야 함
-
공급망 공격 방지를 위해 의존성 업데이트에 쿨다운 기간을 두는 것이, 0-day를 막기 위해 최신 버전을 즉시 쓰는 것보다 낫다는 가정이 있음
업데이트가 새로운 취약점을 도입할 확률과 기존 취약점을 고칠 확률의 비교임
SemVer 기준으로 패치 버전은 상대적으로 안전하므로 짧은 쿨다운을 두는 식의 접근도 가능함
예를 들어 2.3.4에서 2.4.0이 나왔을 때, 급한 기능이 없다면 2.4.1이 나올 때까지 기다리는 게 낫다는 식임- 공개된 0-day는 보안 권고(advisory) 가 나오므로, Dependabot 같은 도구는 쿨다운을 무시하고 즉시 패치함
대부분의 취약점은 의도적 공격이 아니라 일반적인 버그에서 비롯됨 - 기본값은 항상 가정에 기반함. 새로운 정보가 생기면 바꾸면 됨
- 공개된 0-day는 보안 권고(advisory) 가 나오므로, Dependabot 같은 도구는 쿨다운을 무시하고 즉시 패치함
-
의존성의 수와 복잡도를 제한하는 정책이 더 강력한 접근임
“모든 걸 다 하는 라이브러리” 대신, 작고 명확한 목적의 의존성만 추가해야 함
또한 라이브러리들도 LTS 버전을 제공해 보안 패치만 포함하도록 하면 관리가 쉬워짐- 이런 주장은 자주 나오지만, 실제로는 검증된 코드 재사용이 훨씬 효율적임
직접 다시 구현하는 건 낭비일 수 있음. 문제적 “everything library”의 구체적 예시가 궁금함 - 공급망 공격의 대부분은 사회공학적 공격면에서 발생함
같은 개발자를 여러 라이브러리에서 신뢰한다면, 패키지 수보다 신뢰 관계가 더 중요함
복잡도는 취약점과 상관관계가 있지만, 직접적 원인은 아님 -
AI 도구의 등장으로 비핵심 의존성을 직접 구현하는 비용이 줄어듦
이제는 단기 속도보다 장기 유지보수를 고려한 선택이 가능함 - 너무 세분화된 의존성은 오히려 개수와 빌드 부담을 늘릴 수 있음
- 하위 레벨 라이브러리가 다른 의존성을 갖는 건 정당화되기 어려움
C++ 세계에서는 이런 점이 경쟁 포인트가 되기도 함
- 이런 주장은 자주 나오지만, 실제로는 검증된 코드 재사용이 훨씬 효율적임
-
매번 최신 버전으로 업데이트하라는 압박은, 소프트웨어가 항상 더 좋아진다는 잘못된 믿음에서 비롯됨
실제로는 알려진 버그를 새로운 미지의 버그로 바꾸는 것일 수도 있음
공개된 이슈를 모니터링하고 필요할 때만 패치하는 게 합리적임- 예를 들어 log4shell의 경우, log4j 1.x는 취약하지 않았고, 2.x에서 버그가 도입됨
즉, 오래된 버전이 오히려 안전했던 사례임 - 예전에는 릴리스 간격이 길어 품질 향상이 뚜렷했지만, 요즘은 대부분 주변부 변경에 그침
아마도 이미 충분히 안정된 소프트웨어를 쓰고 있어서일 수도 있음
- 예를 들어 log4shell의 경우, log4j 1.x는 취약하지 않았고, 2.x에서 버그가 도입됨
-
모두가 “조금 기다리자”고 하면 결국 공유지의 비극처럼 아무도 먼저 검증하지 않게 됨
기다릴수록 기술 부채가 쌓이므로, 점진적 업데이트와 제로 트러스트·모니터링 같은 완화책이 필요함- 새 버전 공개 후 일주일 정도 기다리는 것은 큰 기술 부채가 아님
그 사이 보안 스캐너들이 이미 취약점을 탐지함 - 최근 공급망 공격들은 소비자 노출이 아니라 유지보수자의 대응 시간 확보로 탐지됨
- 소비자가 줄더라도 연구자와 유지보수자가 분석할 시간을 벌 수 있음
공격자가 무단 릴리스를 올리면 바로 눈치채는 경우도 있음 - 대규모 시스템은 어차피 점진적 롤아웃을 하므로, 즉시 전체 업데이트는 불가능함
- 새 버전 공개 후 일주일 정도 기다리는 것은 큰 기술 부채가 아님
-
쿨다운 아이디어는 좋지만, 공격자가 이를 악용해 가짜 긴급성을 조성할 위험이 있음
“긴급 보안 패치”라며 조기 설치를 유도하고, 그 버전이 실제로는 악성일 수 있음
이런 심리적 압박 공격에 대비해야 함- 공격자가 익스플로잇을 배포할 때 버그 수정도 함께 포함하면, 개발자들이 쿨다운을 어기고 설치하도록 유도할 수 있음
- “어떻게 그런 소음을 만들까?”라는 질문이 나옴 — 즉, 공격자가 여론을 조작하는 방식에 대한 의문임
-
쿨다운의 또 다른 이유는 유지보수자가 스스로 침해 사실을 인지할 시간을 주기 위함임
공격자는 유지보수자가 부재한 시점(휴가, 컨퍼런스, 공휴일 등)을 노림
많은 프로젝트는 한두 명이 관리하므로, 이런 시간차가 매우 중요함 -
프로젝트의 특성과 공격 표면에 따라 다름
단순히 “모범 사례”를 따르는 시대는 끝나야 함
보안은 접촉 스포츠처럼 매일 세부 사항을 비판적으로 생각해야 함 -
단순히 시간이 지나면 안전해진다고 보는 쿨다운의 한계도 있음
아무도 코드를 보지 않으면 일주일이 지나도 위험은 그대로임
대신 점진적 배포(gradual rollout) 방식이 효과적일 수 있음
각 소비자가 지연 기간(delay factor)을 설정해, 위험 감수도가 높은 쪽이 먼저 문제를 맞고,
그 사이 나머지는 보호받는 구조임
위험한 업데이트는 큐에서 제거되어, 지연된 소비자는 아예 보지 않게 됨