터미널에 CVE ID가 나타나고, 의도치 않게 설치한 적이 없는 패키지에 연결되어 있으면, 본능적으로 최악의 상황을 가정하게 됩니다. 우리 모두 그런 경험을 해본 적이 있습니다. 이러한 코드는 사이버 보안 분야에서 일종의 조난 신호와도 같은데, 일단 그 구조를 이해하게 되면 더 이상 무섭게 느껴지지 않고 유용하게 활용될 수 있습니다.
이 주제에 대해서는 이전에도 다룬 적이 있지만, 이번에는 좀 더 깊이 있게 다루고자 합니다. 메타데이터가 실제로 어떤 정보를 제공하는지, 직접 선택하지도 않은 의존성을 통해 결함이 어떻게 드러나는지, 그리고 SecureChain이 패치 작업의 번거로움을 어떻게 크게 덜어주는지 살펴보겠습니다. 이 모든 과정에 걸쳐 한 가지 실제 취약점이 존재하는데, 바로 Rollup 번들러의 CVE-2026-27606입니다.
CVE란 무엇인가요?
예전에는 취약점에 “IIS 경로 가로지르기”와 같이 해당 버그 자체를 지칭하는 고유한 이름이 붙여졌는데, 수십 년에 걸쳐 취약점이 쌓이면서 모든 벤더와 데이터베이스 업체가 똑같은 현상에 대해 각자 다른 이름을 지어내기 전까지는 이 방식이 잘 작동했습니다.
CVE(Common Vulnerabilities and Exposures) 식별자는 공개적으로 알려진 사이버 보안 결함에 부여되는 표준화된 명칭입니다. 이를 통해 보안 전문가와 공급업체는 특정 취약점에 대해 공통된 용어를 사용할 수 있습니다. 이는 외부 구성 요소가 많은 환경에서 특히 중요하며, CVE를 식별함으로써 팀은 누군가가 이를 악용하기 전에 패치 적용 순위를 정하고 결함을 수정할 수 있습니다.
CVE를 온라인으로 주문한 택배의 추적 번호와 비슷하다고 생각해보세요. CVE 자체가 바로 그 버그인 것은 아니지만, 여러분(그리고 다른 모든 사람)이 이 버그가 정확히 무엇인지, 어떤 버전에 영향을 미치는지 파악할 수 있게 해주는 수단입니다.
하지만 두 가지 사항이 끊임없이 혼동되곤 합니다. CVE ID는 단지 식별자일 뿐이며, 심각도 평가를 포함하지 않습니다. 이 ID에 연결된 CVE 기록에는 영향을 받는 버전, 참조 정보, 취약점 분류, 그리고 ID를 할당한 주체가 부여한 심각도 점수 등이 포함될 수 있습니다. 다른 기관들은 여기에 자체 분석을 덧붙이기도 하는데, 이들 간의 견해가 항상 일치하는 것은 아닙니다.
CVE 식별자의 구조
아래 터미널 예시에 나오는 CVE-2026-27606을 예로 들어 보겠습니다. 세 가지 부분으로 구성되어 있습니다:
- CVE: CVE 목록에 등재된 항목임을 나타냅니다.
- 2026년: ID가 할당된 해, 또는 결함이 공개된 해(둘 중 먼저 발생한 해)
- 27606: CVE 번호 부여 기관에서 부여한 일련 번호
사람들이 흔히 오해하는 부분은 바로 이 중간 부분인데, CVE 프로그램의 FAQ는 이를 매우 명확하게 설명하고 있습니다. 연도는 해당 ID가 할당된 시점이나 취약점이 공개된 시점을 나타낼 뿐, 결코 취약점이 발견된 시점을 의미하지 않습니다. 2015년에 공개되었지만 2016년에야 ID가 할당된 취약점도 여전히 CVE-2015라는 번호를 갖습니다. 따라서 연도는 해당 취약점이 공식 기록에 등재된 시점을 나타낼 뿐, 공격자들이 그 취약점을 얼마나 오래 알고 있었는지를 나타내는 것은 아닙니다.
순서 번호는 고유성을 나타내는 것 외에는 아무런 의미가 없습니다. 심각도 등급도 아니고, 대기열 순위도 아니며, 4자리로 고정된 것도 아닙니다. 27606은 5자리이며, 2014년부터 이 형식은 4자리 이상을 허용해 왔습니다.
이러한 번호는 CVE 번호 부여 기관(CNA)에서 발급합니다. MITRE는 1999년에 이 프로그램을 설립했으며 현재도 CISA의 후원을 받아 운영하고 있지만, 번호 부여 업무는 마이크로소프트, 레드햇과 같은 벤더부터 오픈소스 재단 및 버그 바운티 플랫폼에 이르기까지 수백 개의 승인된 CNA에 분산되어 있습니다. 여러분이 보는 모든 CVE 번호를 MITRE가 직접 부여하는 것은 아닙니다. CVE-2026-27606은 GitHub를 통해 등록되었는데, GitHub는 자사가 호스팅하는 프로젝트에 대해 CNA 역할을 수행합니다.
CVE가 공개된 후에는 어떤 일이 일어나는가
공개된 레코드는 모든 사용자에게 공통된 식별자, 설명 및 참조 정보를 제공합니다. 이를 바탕으로 다른 데이터베이스에서 추가적인 맥락 정보를 보완합니다. 국가 취약점 데이터베이스(NVD) 는 CVE 목록과 동기화되며, 레코드에 메타데이터를 추가하여 해당 레코드를 기반으로 조치를 취하기 쉽게 만듭니다. NIST에 따르면, 이는 제출된 참조 자료를 검토하고, CWE를 할당하며, CVSS v3.1 또는 v4.0 지표를 할당하고, CPE 문구를 연결하며, 수석 분석가의 품질 보증(QA) 검사를 수행하는 과정을 의미합니다.
많은 도구들이 아직 따라잡지 못한 부분이 바로 여기입니다. 2026년 4월 15일부터 NIST는 CISA의 KEV 카탈로그에 포함된 기록, 연방 정부 소프트웨어, 그리고 행정명령 14028호에 정의된 중요 소프트웨어 등 세 가지 그룹에 대해 정보 보강을 우선적으로 처리해 왔습니다. 그 외의 모든 항목은 여전히 NVD에 등록되며, 일반적으로 “최저 우선순위(Lowest Priority)”로 표시되어 즉각적인 정보 보강이 예정되어 있지 않습니다. 분석을 요청할 수 있으므로, 이는 엄격한 배제가 아닌 우선순위 지정 방식입니다. 2026년 3월 이전의 미처리 건들도 “예정 없음(Not Scheduled)”으로 분류되었습니다.
따라서 기록에는 NVD에서 산정한 CVSS 점수, CWE 매핑 또는 CPE 구성이 포함되지 않은 채로 도착할 수 있지만, 여전히 해당 기록을 할당한 CNA가 부여한 자체 점수와 취약점 등급은 포함되어 있을 수 있습니다. NVD 필드가 비어 있다고 해서 무해한 버그라는 의미는 아니며, 항상 데이터가 없다는 뜻도 아닙니다. 또한 기록은 공개된 후에도 변경될 수 있으며, NIST는 현재 수정된 기록에 대해 해당 변경 사항이 정보 보강에 실질적인 영향을 미치는 경우에만 재분석하므로, 미루어 두었던 내용은 모두 다시 확인하시기 바랍니다.
그러니 앞으로 끊임없이 마주치게 될 세 가지 약어입니다.
CWE(Common Weakness Enumeration) 는 버퍼 오버플로, 크로스 사이트 스크립팅, 경로 탐색과 같은 오류의 범주를 알려줍니다. CVE가 구체적인 사례라면, CWE는 해당 사례가 속한 패턴을 나타냅니다. CVE-2026-27606에는 CWE-22인 경로 탐색이 포함됩니다. 이러한 범주를 파악하면 다른 곳에서 동일한 오류를 발견하는 데 도움이 되지만, 이는 다른 취약한 사례가 존재한다는 것을 증명하는 것은 아닙니다.
CVSS(공통 취약점 점수 체계)는 기술적 심각도를 나타냅니다. 이는 패치 적용을 미룰 수 있는지 여부를 판단하는 여러 기준 중 하나일 뿐, 조치를 얼마나 시급히 취해야 하는지에 대한 최종 판단이 아니며, 위에서 일방적으로 내려진 단일 수치도 아닙니다. CVE-2026-27606 사례가 이를 잘 보여줍니다. 롤업(Rollup) 권고 페이지에서는 이를 ‘중간(Moderate)’으로 분류한 반면, 자체 영향도 섹션에서는 ‘치명적(Critical)’으로 명시하고 있습니다. CVE 레코드에는 할당 기관인 GitHub의 CVSS v4.0 기준 ‘8.8(높음, High)’이 기재되어 있고, NVD는 v3.1 기준 ‘9.8(치명적, Critical)’으로, 레드햇(Red Hat)의 보안 ADP는 v3.1 기준 ‘9.1(치명적, Critical)’으로 평가했습니다. 단 하나의 버그에 대해 네 가지 서로 다른 등급이 존재하지만, 이들 중 어느 것도 해당 코드가 빌드 컨테이너에서 실행되는지, 아니면 인터넷에 노출된 호스트에서 실행되는지를 고려하지 않았습니다. 단순히 형용사만 보지 말고, 공격 경로와 CVSS 버전을 꼼꼼히 확인하십시오. 악용 가능성에 대해서는 EPSS와 KEV 카탈로그를 참고하십시오.
CPE (공통 플랫폼 열거) 영향을 받는 플랫폼과 버전을 표준화된 방식으로 기술하므로, 해당 기록을 사용자의 환경과 대조하여 확인할 수 있습니다. NVD는 이 CVE를 다음과 같이 매핑합니다. cpe:2.3:a:rollupjs:rollup:*:*:*:*:*:node.js:*:*. 유용하긴 하지만, 실제 재고 현황이라기보다는 데이터베이스 매핑에 해당하며, 현재 가장 누락되기 쉬운 항목입니다.
터미널에서 CVE가 실제로 어떻게 보이는지
이제부터가 진짜입니다. 달려보세요 npm audit, 또는 단순히 패키지를 설치하기만 해도 심각도가 높은 취약점이 7개나 발견될 수 있습니다. 출력 결과에는 CVE 번호 대신 영향을 받는 패키지와 버전이 나열됩니다. 다음은 Angular v18을 설치했을 때의 예시입니다(Angular 16과 같은 구버전의 경우, 의존성 노후화가 1년 동안 누적되어 더 많은 경고가 표시됩니다):
rollup 4.0.0 - 4.58.0
Severity: high
Rollup 4 has Arbitrary File Write via Path Traversal - https://github.com/advisories/GHSA-mw96-cpmx-2vgc
fix available via `npm audit fix --force`
Will install @angular-devkit/[email protected], which is a breaking change
node_modules/rollup
@angular/build 18.1.0-next.0 - 19.2.21 || 20.0.0-next.0 - 20.2.0-rc.1 || 20.3.4 - 20.3.17
Depends on vulnerable versions of rollup
node_modules/@angular/build
@angular-devkit/build-angular *
Depends on vulnerable versions of @angular/build
Depends on vulnerable versions of copy-webpack-plugin
node_modules/@angular-devkit/build-angular
해당 출력 결과에는 CVE ID가 포함되어 있지 않다는 점에 유의하십시오. npm은 GitHub 권고 사항인 GHSA-mw96-cpmx-2vgc를 참조하고 있으며, 이는 CVE-2026-27606에 해당합니다. 번역: rollup이 전이적 의존성으로 포함되었으며, 버전 4.0.0부터 4.58.0까지는 “경로 탐색을 통한 임의 파일 쓰기(Arbitrary File Write via Path Traversal)”라는 심각한 결함이 존재하며, 4.59.0에서 4.x 계열의 이 결함이 수정되었습니다.
이건 흔한 실수입니다. Rollup의 파일명 정제 기능이 유효하지 않은 문자 목록에서 마침표와 두 개의 슬래시를 제외한 나머지 문자들을 제거했기 때문에, ../ 지나갔고, 그 후 ~에게 넘겨졌다 path.resolve. CLI 명명 입력, 청크 별칭 또는 악성 플러그인을 통해 출력 파일 이름을 제어할 수 있는 사람은 누구나, 빌드 프로세스가 접근 권한을 가진 곳이라면 어디에나 파일을 작성할 수 있습니다. 덮어쓰기 ~/.ssh/authorized_keys 또는 쉘 프로파일로 인해 지속적인 코드 실행으로 이어질 수 있습니다.
이것은 소프트웨어 공급망 취약점의 전형적인 사례입니다. 하나의 패키지를 설치했더니, 롤업 패키지가 함께 설치된 것입니다. 의존성 경로를 읽는 법을 익혀 두는 것이 좋습니다.
@angular-devkit/build-angular
└── @angular/build (vulnerable versions)
└── rollup (4.0.0 - 4.58.0) ← the vulnerable package
상황을 더 악화시키지 않고 해결하기
먼저, 해당 취약점이 있는 패키지가 어디에서 실행되는지 파악하십시오. npm audit --omit=dev 이 문서는 devDependencies 없이 설치된 트리에 감사 초점을 맞추고 있는데, Angular 프로젝트의 경우 이는 런타임 종속성을 빌드 툴체인의 상당 부분과 분리합니다. 이는 빌드 시스템이 인증 정보를 보관하고 배포할 결과물을 생성하기 때문에, 빌드 시점의 결함을 무해하게 만드는 것은 아니지만 노출 위험을 재조명합니다.
호환되는 해결 방법부터 시작하세요
일반 npm audit fix npm이 이미 충족할 수 있는 종속성 범위 내에 들어맞는 수정 조치를 적용합니다. 취약한 전이적 패키지를, 최상위 종속성을 선언된 범위 밖으로 벗어나게 하지 않으면서 패치된 버전으로 업데이트할 수 있다면 그렇게 하십시오. 아래의 두 가지 옵션은 그럴 수 없는 경우에만 적용됩니다. 이후의 오류 없는 감사 결과는 앱이 안전하다는 증거가 아니라, 단순히 해결한 소스 트리에서 해당 보안 권고 사항이 사라졌음을 의미할 뿐입니다.
npm audit fix –force 명령어를 강력한 도구처럼 활용하세요
때때로 npm은 수정 사항이 필요하다고 표시합니다. --force, 위와 마찬가지로. npm 문서에서는 해당 플래그가 다음을 허용한다고 설명하고 있습니다. npm audit fix 지정된 종속성 범위 외부의 모듈을 설치할 수 있으며, 여기에는 SemVer의 메이저 버전 변경도 포함됩니다. 여기서는 @angular-devkit/build-angular를 19.2.22로 업그레이드하는데, 이는 빌드 구성 및 API에 영향을 미칠 수 있는 호환성 깨짐 변경 사항으로 표시되어 있습니다. 그럴 수도 있습니다. 또한 빌드가 중단될 수도 있습니다. 실행하기 전에:
- 사용 중인 버전과 대상 버전 간의 @angular-devkit/build-angular 변경 내역을 확인하세요.
- 사용
npm audit fix --dry-run무엇을 옮기려고 하는지 확인하기 위해 - 반드시 새로운 브랜치에서 실행하고, 절대로 main 브랜치에서는 실행하지 마세요.
정당한 사유가 있을 때만 오버라이드를 사용하세요
오버라이드를 사용하면 루트 프로젝트에서 npm이 종속성을 해결하는 방식을 변경하여, 상위 패키지를 업그레이드하지 않고도 취약점이 있는 전이적 패키지를 패치된 버전으로 고정할 수 있습니다. in package.json:
{
"overrides": {
"rollup": "4.59.0"
}
}
실행 npm install, 그런 다음 npm audit 다시. 그런 다음 다음을 실행하세요. npm ls rollup 그리고 실제로 읽어보세요. 오버라이드는 원하는 해결 방안을 명시하는 반면, npm ls 사용자가 받은 버전을 표시하며, 해당 명령어가 적용되면 ‘overridden’ 태그를 부여합니다. 내부적으로는 npm에 “무슨 일이 있어도, 변명 없이, 이 버전을 설치하세요”, 부모가 무엇을 요청했든 상관없이. 이 말은 너무 진지하게 받아들이지 마세요.
오버라이드는 출시해야 할 상황이 있고, 패치된 버전이 존재하며, 빌드를 테스트해 본 결과 아무런 문제가 없음을 확인했을 때만 타당합니다. 그래도 오버라이드는 일시적인 조치여야 합니다. 오버라이드는 기술적 부채를 해소하지 않은 채 경고만 묵음 처리하는 것이기 때문입니다. 왜냐하면 나머지 코드베이스에서 기대하는 버전과 비교해 테스트되지 않은 버전을 실행하고 있는 셈이기 때문입니다. 호환성을 깨지 않는 패치가 있다면 그 방법을 선택하세요. 상위 버전이 패치된 버전을 지원하게 되면 오버라이드를 삭제하세요. 오버라이드는 비상 수단이지, 본래의 계획이 아닙니다. 바로 이것이 ‘한 줌의 소금’으로 받아들여야 할 부분입니다.
규모가 커지면 왜 이 일이 지치게 만드는가
단일 프로젝트의 취약점 하나 정도라면 이 모든 것을 감당할 수 있습니다. 하지만 18개 서비스 중 3개가 2년 전 지원이 종료된 프레임워크 릴리스를 사용하고 있는 상황에서는 더 이상 감당하기 어려워집니다. “패치가 존재한다”는 사실과 “안전하게 적용할 수 있다”는 사실 사이의 간극이야말로 취약점 관리에 막대한 비용이 드는 지점입니다.
이것이 바로 SecureChain이 해결하고자 하는 문제입니다. SecureChain은 각 CVE에 일일이 대응하는 대신, 공급망을 선제적인 보호 계층, 말하자면 ‘갑옷 같은 돔’으로 감싸줍니다. 즉, 패키지는 TuxCare가 관리하는 레지스트리를 통해 검증된 출처에서 서명된 빌드로 제공되며, 새로운 취약점에 대해 지속적으로 패치됩니다. 따라서 사용자는 보안 권고가 발표될 때마다 공개 레지스트리를 상대로 ‘두더지 잡기’ 게임을 하듯 대응할 필요가 없습니다.
이 말에는 두 가지 측면이 있습니다:
- 제품 수명 종료(EOL) 전 보호 조치: 활발히 유지보수 중인 구성 요소의 경우, 엔지니어들이 상류 단계에서 수정 사항을 적용하거나 직접 맞춤형 패치를 구축하여 직접 및 전이적 종속성을 모두 처리합니다.
- 제품 수명 주기 종료(EOL) 후 보호: 상류 지원이 종료된 후에도 ‘무한 수명 주기 지원(ELS) ’을 통해 레거시 소프트웨어에 대한 CVE 패치 제공이 지속되므로, 공급업체가 특정 버전에 대한 지원을 중단하더라도 긴급 업그레이드를 강행할 필요가 없습니다.
두 번째 항목은 겉으로 보이는 것보다 더 큰 의미를 지닙니다. 감사 결과를 망치는 전이적 의존성은 대개 업스트림에서 더 이상 유지 관리하지 않는 것이기 때문입니다. 또한 SecureChain은 특정 패키지 버전에 영향을 미치는 알려진 CVE를 보여주는 기계 가독형 VEX 문서를 공개하는데, 이것이 바로 단순한 권고 사항 목록과 실질적인 해결책의 차이점입니다.
핵심 요점
CVE를 발견했다고 해서 당황할 필요는 없습니다. 이는 문서 기록이 첨부된 추적 번호일 뿐이며, 그 기록은 예전보다 더 간략해졌기 때문에 오히려 더 꼼꼼히 살펴봐야 할 이유가 됩니다.
ID를 해독하세요. 누가 어떤 CVSS 버전을 기준으로 점수를 매겼는지 확인하고, 누락된 항목이 무엇인지 파악하십시오. 취약점 분류에는 CWE를, 기술적 심각도에는 CVSS를, 적용 범위 여부는 CPE 또는 공급업체 데이터를 활용하여 판단하십시오. 그런 다음 트리 구조를 따라 결함을 추적하고, 패치된 버전으로 전환할 수 있는 동시에 업무에 미치는 영향을 최소화하는 수정 방법을 선택하십시오. 숫자를 파악하는 것은 쉬운 부분입니다. 그 이면에 숨겨진 정보를 바탕으로 차분하고, 검증 가능하며, 타당성이 있는 결정을 내리는 것이야말로 진정한 핵심입니다.
