NVD의 취약점 영향 정보, 그대로 믿어도 괜찮을까?
부제: 잘못된 affected version 정보가 취약점 대응에 미치는 영향
시작하기에 앞서..
NVD는 취약점 정보를 제공한다.
그 중에서도 실제 대응에 가장 많이 사용되는 정보 중 하나가 '어떤 제품과 버전이 영향을 받는가'이다.
NVD는 이를 CPE(Common Platform Enumeration)라는 형식으로 표현한다.
예를 들어
cpe:2.3:a:apache:http_server:2.4.49:*:*:*:*:*:*:*
는 Apache HTTP Server 2.4.49를 의미한다.
그리고 NVD는 이러한 CPE를 기반으로 다음을 기록한다.\
- 어떤 버전이 취약한지\
- 어떤 버전에서 수정됐는지
대부분의 취약점 스캐너와 자산 관리 도구는 이 정보를 주로 사용한다.
0. 왜 affected version 정보가 중요한가
취약점 대응은 보통 버전 확인에서 시작된다.
CVE 확인
-> 사용 중인 제품과 버전 확인
-> NVD 또는 벤더 권고문에서 affected version 확인
-> 패치 필요 여부 판단
-> 대응 우선순위 결정
이 과정에서 중요한 질문은 다음과 같다.
우리가 쓰는 이 버전이 실제로 취약한가?
NVD의 CPE 기반 affected software configuration은 이 질문에 답하기 위한 가장 널리 쓰이는 단서 중 하나다. 보안 스캐너와 취약점 관리 시스템은 제품명과 버전을 기준으로 NVD 데이터를 조회하고, 해당 버전이 affected range 안에 있는지 확인한다.
문제는 이 정보가 실제 코드 상태와 항상 일치하지는 않는다는 점이다.
안전한 버전을 취약하다고 판단하면 불필요한 패치와 검증이 발생한다. 반대로 취약한 버전을 안전하다고 판단하면 패치 대상에서 빠진다. 전자는 운영 비용을 늘리고, 후자는 실제 위험을 남긴다.
이 글은 NVD를 쓰지 말자는 이야기가 아니다. NVD는 대부분의 취약점 관리 도구가 기반 정보로 사용하는 중요한 정보이다. 다만 NVD의 affected version 정보는 취약점 대응의 첫 번째 단서로 봐야 한다. 특히 중요한 시스템, 오래된 브랜치, 벤더 포크, LTS 패키지에서는 그 정보가 실제 코드와 맞는지 한 번 더 확인할 필요가 있다.
1. NVD CPE는 무엇을 표현하는가
NVD는 CVE별로 영향을 받는 소프트웨어 구성을 표현하기 위해 CPE를 사용한다. CPE는 벤더, 제품, 버전 등을 구조화된 이름으로 표현하는 방식이다.
NVD의 Vulnerability Detail Page는 CPE Match String과 CPE Match String Range를 이용해 affected configuration을 보여준다. 특히 CPE Match String Range는 단일 버전뿐 아니라 시작 버전과 끝 버전을 이용해 <, >, <=, >= 형태의 범위를 표현할 수 있다. 예를 들어 특정 제품의 1.2.0 이상, 1.2.5 미만 버전을 affected range로 표현하는 식이다. NVD, Understanding Vulnerability Detail Pages
product: libfoo
affected: >= 1.2.0 and < 1.2.5
이 방식은 대규모 취약점 관리에서 매우 유용하다. 모든 CVE마다 소스코드와 패치 이력을 직접 확인하는 것은 현실적으로 어렵다. 이름과 버전으로 먼저 후보를 줄일 수 있어야 한다.
하지만 실제 소프트웨어 배포 구조는 단순한 버전 범위보다 복잡하다.
upstream release
-> release branch
-> LTS branch
-> vendor fork
-> distro package
-> backported patch
-> appliance / firmware build
같은 버전 문자열이라도 실제 포함된 코드가 다를 수 있다. 반대로 서로 다른 버전명이 같은 취약 코드를 포함할 수도 있다. 이 간극 때문에 affected version 정보가 실제 코드 상태와 어긋난다.

2. affected version 정보가 어긋나는 대표적인 상황
affected version 정보가 실제 코드와 맞지 않는 방식은 여러 가지다. 실무에서 특히 문제가 되는 경우는 다음과 같다.
2.1 영향 범위를 너무 넓게 잡는 경우
어떤 취약점이 1.5.0에서 발견됐다고 하자. 데이터베이스에는 다음처럼 기록될 수 있다.
affected: <= 1.5.0
하지만 실제 취약 코드는 1.3.0에서 처음 들어왔을 수 있다. 그러면 1.0.0, 1.1.0, 1.2.0은 영향을 받지 않는다.
이 경우 취약하지 않은 버전까지 패치 대상으로 분류된다. 보안팀은 불필요한 일을 처리하고, 운영팀은 실제로 필요 없는 업그레이드를 검토한다. 이런 알림이 반복되면 스캐너 결과에 대한 신뢰가 떨어지며, 운영 비용 증가로 이어질 수 있다.
안전한 버전까지 affected로 분류
-> 불필요한 패치 검토
-> 알림 피로 증가
-> 취약점 대응 우선순위 혼란
2.2 영향 범위를 너무 좁게 잡는 경우
더 위험한 상황은 반대다.
affected: <= 2.4.1
fixed: 2.4.2
운영팀은 2.4.2로 업그레이드하고 대응을 마쳤다고 판단한다. 하지만 실제로는 패치가 main 브랜치에는 들어갔지만 release/2.4.x 브랜치에는 반영되지 않았을 수 있다. 또는 릴리즈 노트에는 수정됐다고 되어 있지만, 실제 배포 패키지에는 해당 수정이 빠졌을 수 있다.
이 경우 버전 정보상으로는 안전해 보이지만 실제 코드는 여전히 취약하다.
metadata: fixed
code: still vulnerable
이런 오류는 false positive보다 더 위험하다. 취약점이 패치 대상에서 빠지기 때문이다.
2.3 backport가 반영되지 않는 경우
보안 패치는 항상 최신 버전으로만 제공되지 않는다. 오래된 LTS 브랜치나 배포판 패키지에 backport될 수 있다.
main: fixed in 2.0.0
lts/1.4: fixed in 1.4.8-lts
그런데 affected range가 단순히 다음처럼 표현되면 문제가 생긴다.
affected: < 2.0.0
이미 패치된 1.4.8-lts도 취약하다고 분류될 수 있다. 반대로 특정 release branch에만 패치가 빠져 있는데 전체 fixed version으로 처리되는 경우도 있다.
즉, 버전 번호만으로는 보안 패치의 실제 반영 여부를 충분히 설명하기 어렵다.
2.4 제품명, 패키지명, 저장소명이 서로 맞지 않는 경우
CPE는 제품 이름 중심의 식별 체계다. 하지만 실제 개발과 배포는 Maven, npm, PyPI, Go module, distro package, container image, firmware bundle처럼 다양한 단위로 이루어진다.
Google이 OSV를 공개하면서 지적한 문제도 이 지점과 연결된다. Google은 기존 취약점 표준의 versioning scheme, 특히 CPE가 오픈소스의 tag와 commit hash 기반 버전 체계와 잘 맞지 않아 downstream consumer가 영향을 받는 취약점을 놓칠 수 있다고 설명했다. Google Open Source Blog, Launching OSV
현실에서는 다음 항목들이 완전히 일치하지 않을 수 있다.
CPE product name
!= package name
!= source repository
!= distro package
!= deployed binary
이 매핑이 흔들리면 취약점 영향 판단도 흔들린다.
3. 이 문제는 이미 여러 연구에서 확인됐다
NVD의 affected version 정보가 실제 취약점 대응과 어긋날 수 있다는 문제는 오래전부터 연구되어 왔다.
Nguyen과 Massacci의 The (Un)Reliability of NVD Vulnerable Versions Data는 Google Chrome 취약점을 대상으로 NVD의 vulnerable version data를 검토했다. 이 연구는 NVD에 기록된 vulnerable version 정보에 오류가 있을 수 있고, 그 오류가 취약점 분석 결과에도 영향을 줄 수 있음을 보였다. Nguyen and Massacci, ASIA CCS 2013
USENIX Security 2019의 Towards the Detection of Inconsistencies in Public Security Vulnerability Reports는 NVD의 구조화된 데이터와 CVE description, 외부 vulnerability report 사이의 불일치를 다뤘다. 연구진은 VIEM이라는 시스템을 제안해 취약 소프트웨어 이름과 영향 버전 정보가 서로 어긋나는 사례를 자동으로 탐지하려 했다. Dong et al., USENIX Security 2019
이 연구들이 보여주는 메시지는 명확하다.
취약점 데이터베이스의 affected version 정보는 매우 중요하지만,
그 정보가 항상 실제 패치와 코드 상태를 완벽하게 반영하지는 않는다.
NVD가 중요하지 않다는 뜻이 아니다. 오히려 너무 많이 쓰이기 때문에, affected version 정보가 어긋날 때 파급 효과가 커진다.
4. 왜 affected version 식별은 어려운가
겉보기에는 간단해 보인다.
패치가 들어가기 전 버전은 취약하고,
패치가 들어간 뒤 버전은 안전하다.
하지만 실제 취약점과 패치 이력은 이렇게 단순하지 않다.
4.1 보안 패치는 항상 취약 코드를 삭제하지 않는다
가장 이해하기 쉬운 패치는 다음과 같다.
- dangerous_call(input);
+ safe_call(input);
이 경우 삭제된 라인을 보면 취약 코드의 위치를 어느 정도 추정할 수 있다.
하지만 많은 보안 패치는 위험한 코드를 삭제하지 않는다. 대신 검증 로직을 추가한다.
+ if (len > max_len)
+ return ERROR;
memcpy(buf, input, len);
이 경우 memcpy는 그대로 남아 있다. 달라진 것은 그 전에 입력 길이를 검사하는 흐름이다.
따라서 “패치에서 삭제된 라인 = 취약 코드”라는 가정은 모든 취약점에 적용되기 어렵다. 취약점은 특정 한 줄이 아니라, 검증되지 않은 입력이 위험한 연산까지 도달하는 흐름일 수 있다.
4.2 브랜치와 backport는 단순 버전 비교를 어렵게 만든다
취약점은 보통 upstream에서 먼저 패치된다. 그러나 실제 운영 환경은 LTS branch, vendor fork, distro package, appliance firmware를 사용할 수 있다.
확인해야 할 것은 단순한 버전 번호가 아니다.
upstream fixed commit이 있는가?
그 commit이 내가 쓰는 release branch에 포함되었는가?
backport patch가 별도로 적용되었는가?
배포 패키지에 실제로 반영되었는가?
버전 번호가 높다고 항상 안전한 것도 아니고, 버전 번호가 낮다고 항상 취약한 것도 아니다.
4.3 코드가 변형되면 단순 매칭이 흔들린다
소프트웨어는 계속 바뀐다. 함수 이름이 바뀌고, 코드 위치가 이동하고, 변수명이 바뀌고, 로직이 리팩토링된다.
문자열로는 달라 보이지만 취약한 동작은 그대로 남을 수 있다. 반대로 코드 일부가 비슷해 보여도 실제 취약 조건은 사라졌을 수 있다.
그래서 affected version 식별은 단순한 문자열 검색이 아니다. 패치의 의미, 코드 이력, 릴리즈 구조를 함께 봐야 한다.
5. 연구들은 이 문제를 어떻게 완화하려 했나
affected version 문제를 다루는 연구들은 서로 다른 관점에서 같은 질문을 다룬다.
이 CVE가 실제로 영향을 주는 버전은 어디까지인가?
여기서 중요한 것은 특정 연구 하나가 모든 문제를 해결했다는 뜻이 아니다. 각 연구는 NVD affected version 정보만으로 판단하기 어려운 부분을 다른 방식으로 보완하려 했다.
5.1 SZZ 계열: 취약점이 언제 들어왔는가
SZZ는 원래 bug-fixing commit을 보고 bug-introducing commit을 찾기 위해 쓰이던 아이디어다. 취약점 분석에서는 이를 “취약점이 처음 들어온 commit을 찾자”는 문제로 확장할 수 있다.
ICSE 2022의 V-SZZ: Automatic Identification of Version Ranges Affected by CVE Vulnerabilities는 이 방향의 대표적인 연구다. V-SZZ는 vulnerability-fixing commit에서 취약점과 관련된 라인을 추적해 vulnerability-inducing commit을 찾고, 이를 기반으로 affected version range를 다시 계산하려 했다. Bao et al., ICSE 2022
이 접근은 NVD의 버전 범위를 그대로 따르는 대신, 코드 이력에서 취약점의 시작점을 찾으려 한다는 점에서 의미가 있다.
다만 한계도 있다. 패치가 add-only 형태이거나, 취약 로직이 한두 줄로 명확히 남아 있지 않거나, 리팩토링으로 코드가 크게 바뀐 경우에는 추적이 어려워진다. SZZ 계열은 “언제 들어왔는가”를 보는 데 강하지만, 모든 형태의 패치와 코드 변화를 포괄하기는 어렵다.
5.2 origin 추적: 취약점은 어디서 시작됐는가
USENIX Security 2021의 V0Finder는 “Vulnerability Zero”라는 개념을 제시했다. 관심사는 특정 취약점이 어떤 소프트웨어와 버전에서 처음 시작되었는가다. V0Finder는 코드 재사용 관계를 분석해 취약점이 어떤 방향으로 전파되었는지 추적한다. Woo et al., USENIX Security 2021
이 관점은 CPE 문제와 연결된다. 취약점은 한 프로젝트 안에서만 움직이지 않는다.
upstream library
-> fork
-> downstream project
-> vendor SDK
-> product firmware
제품 이름과 버전만 보면 이런 전파 경로를 놓칠 수 있다. V0Finder 같은 연구는 취약점의 출처와 재사용 관계를 코드 관점에서 보려는 시도다.
다만 origin을 찾는 것과 모든 release branch의 affected 여부를 판단하는 것은 다르다. 취약점이 어디서 시작됐는지 알아도, 각 downstream 버전이 실제로 패치됐는지는 다시 확인해야 한다.
5.3 affected / unaffected version을 직접 판단하려는 연구
다음 흐름은 더 직접적이다.
이 특정 버전이 affected인가, unaffected인가?
ASE 2022의 Precise (Un)Affected Version Analysis for Web Vulnerabilities는 웹 취약점을 대상으로 patch commit과 vulnerability fingerprint를 이용해 특정 버전이 affected인지, unaffected인지 구분하려 했다. 여기서 중요한 점은 단순히 “취약한 버전을 더 많이 찾는 것”이 아니다. 취약하지 않은 버전까지 정확히 구분해야 불필요한 대응을 줄일 수 있다. Shi et al., ASE 2022
Java 생태계에서는 VERJava가 비슷한 문제를 다룬다. VERJava는 Java OSS 취약점에 대해 function-level과 patch-level 분석을 결합해 실제 취약 버전을 식별하려 한다. 특정 언어와 생태계에 맞게 패치 라인, 함수 단위 변화, 버전 이력을 함께 보려는 접근이다. Sun et al., ICSME 2022
ASE 2024의 VISION은 open-source vulnerability에 대해 affected library versions를 식별하려는 연구다. 이 연구는 취약점 설명이나 패치 정보에서 단서를 얻고, 라이브러리 버전별로 취약 로직이 남아 있는지 판단하려 한다. 즉 “이 라이브러리의 어느 버전까지 영향을 받는가”에 더 직접적으로 다가간다. Wu et al., ASE 2024
C/C++ 취약점에서도 유사한 흐름이 이어진다. 예를 들어 Clovery는 public vulnerability report의 affected version 정보를 확인하기 위해 patch와 repository를 함께 보고, 함수 이력과 코드 의미 관계를 이용해 버전별 영향을 판단하려 한다. 핵심은 특정 버전 문자열만 보는 것이 아니라, 실제 코드에 취약한 흐름이 남아 있는지 확인하려는 데 있다. Kim et al., IFIP SEC 2026
이 연구들의 공통점은 분명하다.
문서에 적힌 affected range만 보지 않고,
패치와 코드 상태를 이용해 버전별 영향을 다시 판단한다.
다만 이 방향도 쉬운 문제는 아니다. patch commit을 정확히 찾기 어려울 수 있고, 취약점 설명이 충분하지 않을 수 있으며, 언어마다 분석 가능한 정보가 다르다. 함수 이름이 바뀌거나 코드가 리팩토링되면 단순 매칭은 쉽게 흔들린다. add-only patch처럼 취약 코드를 삭제하지 않고 검증 로직만 추가하는 패치도 많다. 결국 affected version 식별은 “패치가 있는가”만 확인하는 문제가 아니라, 패치가 어떤 취약한 동작을 막는지 이해해야 하는 문제에 가깝다.
5.4 현 연구 상황
여러 연구가 등장했지만, 결론을 “자동 도구가 NVD affected range 문제를 해결해준다”로 가져가면 안 된다. 최근 연구들은 오히려 이 문제가 아직 충분히 어렵다는 점을 보여준다.
ASE 2025의 Vulnerability-Affected Versions Identification: How Far Are We?는 이 분야의 현재 상황을 정리하는 연구다. 이 논문은 vulnerability-affected versions identification 도구들을 크게 tracing 계열과 matching 계열로 나누고, C/C++ 취약점 benchmark에서 여러 도구를 비교한다. tracing 계열은 SZZ처럼 취약점이 들어온 commit을 역추적해 affected version을 추론하고, matching 계열은 취약 로직이나 패치 패턴을 여러 버전에서 직접 찾아보는 방식이다. Chen et al., ASE 2025
도구가 많아졌고 접근도 다양해졌지만, 실제 취약점 이력은 여전히 자동화하기 어렵다. add-only patch, cross-file change, multi-branch development, 제한적인 semantic reasoning, rigid matching logic 같은 요소가 계속 발목을 잡는다. 즉, NVD affected version 정보가 틀릴 수 있다는 문제를 완화하려는 연구는 분명히 진행되고 있지만, 아직 하나의 방식으로 모든 상황을 처리하기는 어렵다.
그래서 이 분야의 흐름은 단순히 더 빠른 매칭으로 가지 않는다. 패치가 어떤 의미를 갖는지, 취약 로직이 버전 이력에서 어떻게 변했는지, 브랜치와 backport가 어떻게 반영되었는지, 생태계별 버전 표현이 어떤 차이를 갖는지를 함께 보려는 방향으로 이동하고 있다.
결국 연구 흐름을 한 문장으로 정리하면 이렇다.
NVD의 affected version 정보는 출발점으로 유용하지만,
중요한 판단에는 패치와 코드 이력을 함께 확인해야 한다.
6. 실무에서는 어떻게 봐야 하나
NVD CPE를 버릴 필요는 없다. 오히려 대규모 취약점 대응에서는 반드시 필요하다.
다만 역할을 다르게 봐야 한다.
NVD CPE = 빠른 후보 탐색
패치, 브랜치, 벤더 권고문, 실제 코드 = 판단 보강
특히 다음 상황에서는 affected range만 보고 결론을 내리기 어렵다.
- LTS branch를 사용한다
- vendor fork 또는 distro package를 사용한다
- upstream과 다른 버전명을 사용한다
- backport patch가 적용되었다
- firmware나 appliance 내부에 OSS가 번들되어 있다
- CVE description과 vendor advisory가 서로 다르게 말한다
- fixed version으로 올렸지만 실제 patch commit 포함 여부가 불명확하다
이런 경우에는 다음 질문을 확인해야 한다.
1. NVD가 말하는 affected range는 어떤 CPE match 기준인가?
2. vendor advisory도 같은 범위를 말하는가?
3. fixed version이라고 표시된 릴리즈에 실제 patch commit이 포함되어 있는가?
4. 우리가 쓰는 release branch나 패키지에도 그 패치가 들어왔는가?
5. backport가 있다면 version range보다 patch presence를 우선해서 봐야 하는가?
6. 제품 안에 포함된 코드가 upstream release와 다른가?
OSV 같은 생태계 중심 취약점 데이터베이스를 함께 보는 것도 도움이 된다. OSV는 package ecosystem, introduced/fixed event, commit 또는 version range를 구조화해서 표현하려 한다. OpenSSF의 OSV schema 문서도 이를 open source package vulnerability를 위한 표준 interchange format으로 설명한다. OpenSSF OSV Schema
좋은 취약점 대응은 하나의 데이터 소스만 보는 것이 아니다.
NVD CPE
+ vendor advisory
+ OSV / ecosystem advisory
+ patch commit
+ release branch
+ 실제 배포 artifact
이 정보들을 함께 봐야 한다.
7. 결론: NVD만 보기에는 소프트웨어가 너무 복잡하다
NVD의 CPE 기반 affected version 정보는 취약점 관리에서 매우 유용하다. 대규모 환경에서 위험 후보를 빠르게 찾는 데 필요하다.
하지만 그 정보가 항상 실제 코드 상태와 맞는 것은 아니다. 영향 범위가 너무 넓게 잡힐 수도 있고, 너무 좁게 잡힐 수도 있다. backport와 release branch를 놓칠 수 있고, 제품 이름과 패키지 이름이 어긋날 수도 있다. fixed version이라고 표시되어 있어도 실제 운영 환경의 코드에는 패치가 없을 수 있다.
이 문제를 줄이기 위해 여러 연구가 등장했다. V-SZZ는 취약점이 언제 들어왔는지 코드 이력에서 찾으려 했고, V0Finder는 취약점의 origin과 전파 관계를 보려 했다. AFV, VERJava, VISION, Clovery 같은 연구들은 패치와 코드 상태를 이용해 특정 버전이나 라이브러리가 실제로 affected인지 판단하려는 흐름에 있다. 최근의 Vulnerability-Affected Versions Identification: How Far Are We?는 이런 도구들을 tracing 계열과 matching 계열로 정리하며, affected version 식별이 여전히 패치 구조, 브랜치, 코드 변화, 제한적인 의미 분석 때문에 어려운 문제임을 보여준다.
결국 핵심은 간단하다.
NVD를 믿지 말자는 것이 아니다.
NVD만 보고 끝내지 말자는 것이다.
취약점 대응에서 중요한 판단을 내릴 때는 한 번 더 확인해야 한다.
이 버전이 안전하다는 판단은 단순한 버전 범위에서 온 것인가,
아니면 실제 패치와 코드 상태까지 확인한 것인가?
그 차이가 불필요한 패치 비용을 줄이고, 놓친 취약점이 운영 환경에 남는 일을 줄인다.