보안

NVD·OSV 차이와 KEV·EPSS — 컴포넌트 취약점 어디서 조회하나

NVD·OSV 차이와 KEV·EPSS — 컴포넌트 취약점 어디서 조회하나

쓰는 컴포넌트에 CVE 가 떴는지 정기적으로 확인해야 한다. 흔히 두 곳을 잡아 둔다.

  • NVD — https://services.nvd.nist.gov/rest/json/cves/2.0
  • OSV — https://api.osv.dev/v1/query

주소는 둘 다 맞다. 그런데 "NVD 우선, OSV 보완" 이라는 순서는 2026년 기준으로
더 이상 맞지 않는다.
NVD 가 2026년 4월에 운영 방침을 바꿨기 때문이다.

이 글은 그 변화를 확인하고, 그래서 무엇을 더 봐야 하는지까지 정리한다.

소스 무엇에 쓰나
CVE List (cvelistV5) CVE 원본 기록. 가장 빠르고 빠짐이 없다
NVD CPE 매칭과 NVD 자체 분석. 2026년부터 전수 분석이 아니다
OSV 패키지·버전 단위 판정. 언어 생태계 + 리눅스 배포판
GitHub Advisory 언어 패키지 권고. 영향 버전 범위가 정확하다
배포판 보안 트래커 컨테이너 베이스 이미지의 오탐을 걸러 낸다
CISA KEV 실제로 악용 중인 취약점. 우선순위 1순위
EPSS 악용될 확률. 남은 것들의 순서를 정한다

참고

이 글의 API 주소와 파라미터는 각 서비스의 공식 문서로 확인한 것이다. 다만
글을 쓴 환경에서 이 API 들로 나가는 통신이 막혀 있어, 응답을 직접 받아
붙이지는 못했다.
명령은 그대로 쓰면 되지만 출력은 각자 확인한다.

1. NVD — 주소는 맞고, 전제가 바뀌었다#

https://services.nvd.nist.gov/rest/json/cves/2.0 이 맞다. CVE 하나를 보는
가장 짧은 형태는 이렇다.

curl -s "https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2021-44228" | jq .

정기 점검에 쓸 만한 파라미터는 이 정도다.

파라미터 쓰임
cveId CVE 하나를 콕 집어 조회
cpeName CPE 로 특정 제품·버전에 걸린 CVE
keywordSearch 설명 문구 검색
lastModStartDate / lastModEndDate 마지막 조회 이후 바뀐 것만 — 배치에 이걸 쓴다
startIndex / resultsPerPage 페이징

API 키를 받는다#

한도가 꽤 빡빡하다.

한도
키 없이 30초 롤링 윈도우당 5회
키 발급 후 30초 롤링 윈도우당 50회

키는 nvd.nist.gov/developers/request-an-api-key
에서 받고, 요청에 apiKey 헤더로 넣는다.

curl -s -H "apiKey: $NVD_API_KEY" 
  "https://services.nvd.nist.gov/rest/json/cves/2.0?lastModStartDate=2026-09-01T00:00:00.000&lastModEndDate=2026-09-08T00:00:00.000"

키가 있어도 NIST 는 요청 사이에 몇 초씩 쉬라고 권한다. 컴포넌트 200개를
한 번에 돌리는 배치라면 이 대기 시간부터 계산에 넣는다.

여기가 중요하다 — 2026년 4월의 방침 변경#

NIST 는 2026년 4월 15일부터 들어오는 CVE 를 전부 분석하지 않는다. CVE 접수량이
2020년에서 2025년 사이 263% 늘어난 것이 이유다. 전면 분석 대상은 셋으로 좁혀졌다.

  1. CISA KEV 카탈로그에 오른 CVE
  2. 미 연방정부가 쓰는 소프트웨어의 CVE
  3. 행정명령 14028 이 정의한 핵심 소프트웨어의 CVE

여기에 못 드는 것은 "Lowest Priority — not scheduled" 로 분류된다. 밀려 있던
약 29,000건도 이 상태로 넘어갔다. 게다가 CNA 가 이미 CVSS 를 매긴 CVE 에는
NVD 가 자기 점수를 따로 붙이지 않는다.

주의

실무에서 이게 무슨 뜻이냐면 — NVD 에서 CVSS 가 안 나온다고 "심각하지 않다"
가 아니다.
"아무도 분석하지 않기로 했다" 일 수 있다. NVD 응답만 보고
심각도를 판정하는 파이프라인이 있다면, 점수가 비어 있을 때 어떻게 할지를
정해 두어야 한다. 조용히 통과시키면 안 된다.

NVD 를 버리라는 얘기가 아니다. CPE 매칭은 여전히 NVD 가 제일 낫다. 다만
"공식이니까 여기가 최신이고 제일 완전하다" 는 전제만 버린다.

2. OSV — 주소도 맞고, 지금은 이쪽이 더 실용적이다#

https://api.osv.dev/v1/query 가 맞다. 인증이 없고, 패키지 이름과 버전을
그대로 물어볼 수 있다
는 게 핵심이다. CPE 문자열을 만들 필요가 없다.

curl -s -X POST "https://api.osv.dev/v1/query" -d '{
  "package": {"name": "express", "ecosystem": "npm"},
  "version": "4.17.1"
}' | jq '.vulns[].id'

CVE 하나만 보려면 GET 이다.

curl -s "https://api.osv.dev/v1/vulns/CVE-2021-44228" | jq .

엔드포인트는 셋이다.

엔드포인트 메서드 쓰임
/v1/query POST 패키지+버전 하나, 또는 커밋 해시로 조회
/v1/querybatch POST 여러 개를 한 번에 — 한 요청에 최대 1,000건
/v1/vulns/{id} GET ID 로 상세 조회

querybatch 는 ID 와 수정 시각만 돌려주고, 입력과 같은 순서로 온다. 상세가
필요한 것만 골라 /v1/vulns/{id} 로 한 번 더 부른다. 개별 질의가 1,000건을
넘거나 전체가 3,000건을 넘으면 페이지가 나뉘고 next_page_token 이 붙는다.

팁

OSV 는 이제 언어 생태계만 다루지 않는다. Debian, Ubuntu, Alpine, Red Hat,
Rocky, AlmaLinux, SUSE/openSUSE, Chainguard/Wolfi 까지 버전 질의가 된다.
컨테이너 베이스 이미지 점검을 여기서 같이 처리할 수 있다.

3. 빠진 소스들#

두 곳으로는 "이게 진짜 문제인가" 를 판단할 수 없다. 다음이 그 자리를 메운다.

CVE 원본 — cvelistV5#

CVE 기록 자체는 CVE Program 이 GitHub 저장소로 낸다.
github.com/CVEProject/cvelistV5 가
공식 캐시이고 약 7분마다 갱신된다. NVD 가 분석을 미뤄도 원본 기록은 여기 있다.

git clone --depth 1 https://github.com/CVEProject/cvelistV5.git

예전 CSV·XML·CVRF 다운로드는 2024년 6월 30일로 전부 끝났다. 그 형식을 긁던
스크립트가 남아 있다면 지금 동작하지 않는다.

GitHub Advisory Database#

curl -s "https://api.github.com/advisories?ecosystem=npm&severity=critical" | jq '.[].ghsa_id'

공개 권고만 볼 거면 인증 없이도 된다. 언어 패키지의 영향 버전 범위가
정확해서, "우리가 쓰는 4.17.1 이 진짜 걸리는가" 를 판정하기 좋다.

참고

기본 응답에는 악성 패키지(malware)가 빠져 있다. 공급망 쪽까지 보려면
type=malware 를 따로 붙여야 한다.

배포판 보안 트래커 — 컨테이너 쓰면 필수#

스캐너가 컨테이너 이미지를 "취약" 이라고 하는데 실제로는 아닌 경우가 많다.
배포판이 패치만 뒤로 이식(backport)하고 버전 번호는 그대로 두기 때문이다.
업스트림 버전 숫자만 보면 안 고쳐진 것처럼 보인다.

같은 CVE-2023-0464 를 NVD 는 HIGH 로, Red Hat 은 Low 로 본다. 어느 쪽이 틀린 게
아니라 보는 대상이 다르다. 그래서 Trivy 같은 스캐너는 배포판 권고를 NVD 보다
우선한다.

배포판 트래커
Debian security-tracker.debian.org
Ubuntu ubuntu.com/security/cves
Red Hat access.redhat.com/security/security-updates/cve (OVAL 제공)

Red Hat 계열은 버전만이 아니라 NEVRA(Epoch:Version-Release) 전체를 OVAL 과
맞춰야 backport 가 제대로 반영된다.

CISA KEV — 우선순위의 출발점#

실제로 악용이 확인된 취약점 목록이다. 하루 한 번 받아서 우리 CVE 목록과
교집합을 내면, 오늘 당장 손볼 것이 나온다.

curl -s "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json" 
  | jq -r '.vulnerabilities[].cveID' > kev.txt
comm -12 <(sort our-cves.txt) <(sort kev.txt)

cisa.gov/kev 로도 들어가진다. 미러가 필요하면
github.com/cisagov/kev-data 가 같은 데이터를
CSV·JSON 으로 낸다.

KEV 는 NVD 방침 변경과도 직접 엮인다. KEV 에 오른 CVE 는 NVD 가 계속 전면
분석하는 세 부류 중 하나다. 뒤집어 말하면 KEV 밖의 CVE 는 NVD 분석을 기대하기
어렵다.

EPSS — 나머지의 순서#

KEV 는 "이미 악용 중" 만 담는다. 그 밖의 수천 건 순서는 EPSS 로 정한다. FIRST 가
매일 갱신하고 인증이 없다.

curl -s "https://api.first.org/data/v1/epss?cve=CVE-2021-44228" | jq '.data[]'

두 값이 나온다.

  • epss — 앞으로 30일 안에 악용될 확률 (0~1)
  • percentile — 전체 CVE 중 백분위

CVSS 9.8 인데 EPSS 0.0004 인 것과, CVSS 6.5 인데 EPSS 0.7 인 것 중 뭘 먼저
고쳐야 하는지는 명확하다. CVSS 는 심각도이지 시급성이 아니다.

국내 — KNVD, 보호나라#

국내 공지가 필요하면 KISA 의 취약점 정보 포털 knvd.krcert.or.kr 과 보호나라
보안공지를 본다. 다만 이쪽은 웹 페이지 기준이고, 공개 REST API 는 확인하지
못했다.
자동화가 필요하면 위 국제 소스를 파이프라인에 두고, 국내 공지는 사람이
읽는 쪽으로 분리하는 편이 현실적이다.

4. 그래서 어떻게 조합하나#

세 단으로 나눈다. 한 소스가 전부를 하려 들면 실패한다.

1단 — 무엇이 걸렸나 (탐지)

컴포넌트 목록(SBOM)을 OSV querybatch 에 밀어 넣는다. 컨테이너 이미지면 배포판
권고까지 OSV 가 같이 본다. 언어 패키지는 GitHub Advisory 로 버전 범위를 한 번 더
확인한다.

2단 — 진짜인가 (검증)

배포판 트래커에서 backport 여부를 본다. 여기서 걸러지는 게 컨테이너 오탐의 대부분이다.
상세가 필요하면 NVD 로 CPE 매칭을, 원본이 필요하면 cvelistV5 를 본다.

3단 — 뭘 먼저 (우선순위)

KEV 교집합을 최우선으로 올린다. 남은 것은 EPSS 로 줄을 세운다. CVSS 는 그다음이다.

주의

NVD 를 1단(탐지)에 두지 않는다. 2026년 4월 이후 NVD 는 전수 분석을 하지
않으므로, 탐지 기준을 NVD 로 잡으면 분석되지 않은 CVE 를 통째로 놓친다.
NVD 는 2단(검증)에서 쓴다.

확인#

파이프라인을 짜고 나면 아는 CVE 하나로 전 구간을 통과시켜 본다. Log4Shell
(CVE-2021-44228) 이 편하다 — 모든 소스에 다 있고 KEV 에도 올라 있다.

CVE=CVE-2021-44228
echo "--- OSV ---";  curl -s "https://api.osv.dev/v1/vulns/$CVE" | jq -r '.id, .severity'
echo "--- NVD ---";  curl -s -H "apiKey: $NVD_API_KEY" 
  "https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=$CVE" | jq -r '.totalResults'
echo "--- EPSS ---"; curl -s "https://api.first.org/data/v1/epss?cve=$CVE" | jq -r '.data[].epss'
echo "--- KEV ---";  curl -s "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json" 
  | jq -r --arg c "$CVE" '.vulnerabilities[] | select(.cveID==$c) | .dateAdded'

네 줄이 다 값을 내면 연결은 끝난 것이다. 그다음엔 일부러 최근 CVE 로도 한 번
해 본다.
NVD 만 비어 나오는 경우를 직접 봐 두면, 위에서 말한 "점수가 없는 것과
안전한 것은 다르다" 가 손에 남는다.

NVD 한도도 확인해 둔다. 키 없이 30초에 6번을 부르면 막힌다. 배치가 컴포넌트
수에 비례해 커진다면 여기서 먼저 터진다.

정리#

  • 두 주소는 맞다. services.nvd.nist.gov/rest/json/cves/2.0 과 api.osv.dev/v1/query 둘 다 유효하다.
  • "NVD 우선" 이라는 순서만 뒤집는다. 2026년 4월부터 NVD 는 KEV·연방 소프트웨어·EO 14028 핵심 소프트웨어만 전면 분석한다. 탐지는 OSV 로, NVD 는 검증용으로 쓴다.
  • 점수가 없는 것과 안전한 것을 구분한다. NVD 응답의 빈 CVSS 를 "이상 없음" 으로 처리하는 코드가 있다면 지금 고친다.
  • 탐지와 우선순위는 다른 소스가 답한다. 무엇이 걸렸는지는 OSV 가, 뭘 먼저 고칠지는 KEV 와 EPSS 가 답한다. CVSS 하나로 줄을 세우면 순서가 틀린다.
  • 컨테이너를 쓴다면 배포판 트래커는 선택이 아니다. backport 를 모르면 스캐너 결과의 상당수가 오탐이고, 그 오탐이 진짜를 덮는다.

참고

사내에 Black Duck 같은 상용 SCA 가 이미 있다면, 그건 이 API 들과 하는 일이
다르다.
여기 소스들은 이름과 버전을 알아야 물어볼 수 있고, 상용 SCA 는
파일에서 컴포넌트를 먼저 알아낸다. 그 차이는
Black Duck 은 어떻게 검사하나에서 따로 다룬다.

참고#

공유
욱콩

욱콩

서버와 배포를 만지면서 겪은 것을 기록합니다. 직접 돌려 보고 나온 결과만 씁니다.

댓글

댓글 남기기