SBOM(Software Bill of Materials)은 소프트웨어에 포함된 구성요소 목록이다. 어떤 패키지가 어떤 버전으로 들어 있는지, 라이선스는 무엇인지를 기계가 읽을 수 있는 형식으로 기록한다.
SBOM의 가치는 생성 시점이 아니라 사건이 터졌을 때 드러난다. 새 취약점이 공개됐을 때 "우리 시스템 중 영향받는 것이 무엇인가"를 몇 분 안에 답할 수 있는가. SBOM이 없으면 이 질문에 며칠이 걸린다.
처음 SBOM을 도입했을 때 나는 그게 규제 대응용 문서라고 생각했다. 인식이 바뀐 건 널리 쓰이는 라이브러리에 치명적 취약점이 공개된 날이었다. 우리가 그걸 쓰는지, 어느 서비스가 쓰는지, 어떤 버전인지를 파악하는 데 꼬박 이틀이 걸렸다. 그 이틀 동안 아무것도 못 했다.
형식과 도구
| 항목 | 선택지 | 비고 |
|---|---|---|
| 형식 | SPDX, CycloneDX | 둘 다 널리 쓰인다. 도구 지원 범위로 선택 |
| 생성 | syft 등 스캐너 | 컨테이너 이미지·디렉터리·아카이브 지원 |
| 취약점 매칭 | grype 등 | SBOM 또는 이미지를 입력으로 취약점 조회 |
| 서명·배포 | cosign 등 | SBOM 자체의 무결성도 보장 대상 |
| 보관 | 아티팩트 저장소·레지스트리 | 빌드 산출물과 함께 버전별로 |
# 컨테이너 이미지에서 SBOM 생성
syft registry.example.com/app:1.4.0 -o cyclonedx-json > sbom.cdx.json
# 소스 디렉터리에서 생성 (빌드 전 의존성 기준)
syft dir:. -o spdx-json > sbom.spdx.json
# SBOM 으로 취약점 스캔 (이미지 재스캔 없이 빠르게)
grype sbom:./sbom.cdx.json --fail-on high
# 이미지 직접 스캔
grype registry.example.com/app:1.4.0 --only-fixed
# 결과를 기계가 읽을 형식으로
grype sbom:./sbom.cdx.json -o json > vulns.json
언제 생성할 것인가
생성 시점에 따라 담기는 내용이 달라진다. 이 차이를 모르고 만들면 정작 필요할 때 정보가 빠져 있다.
| 시점 | 포함되는 것 | 빠지는 것 |
|---|---|---|
| 소스 스캔 | 선언된 의존성 | 실제 빌드에 들어간 전이 의존성 일부 |
| 빌드 직후 | 빌드 산출물의 실제 구성 | 런타임에 주입되는 것 |
| 이미지 스캔 | OS 패키지 + 애플리케이션 의존성 | 동적으로 내려받는 구성요소 |
| 런타임 관찰 | 실제 로드된 라이브러리 | 실행되지 않은 경로의 구성요소 |
실무 권장은 이미지 빌드 직후다. OS 패키지와 애플리케이션 의존성이 함께 잡히고, 배포 산출물과 1:1로 대응한다. 여기에 소스 단계 SBOM을 보조로 두면 빌드 과정에서 추가·제거된 것을 비교할 수 있다.
파이프라인 통합
# CI 예시 (GitHub Actions 형태)
- name: Build image
run: docker build -t app:${{ github.sha }} .
- name: Generate SBOM
run: |
syft app:${{ github.sha }} -o cyclonedx-json > sbom.cdx.json
syft app:${{ github.sha }} -o spdx-json > sbom.spdx.json
- name: Scan for vulnerabilities
run: |
grype sbom:./sbom.cdx.json --fail-on critical -o table
grype sbom:./sbom.cdx.json -o json > vulns.json
- name: Attach SBOM to image (attestation)
run: |
cosign attest --predicate sbom.cdx.json \
--type cyclonedx registry.example.com/app:${{ github.sha }}
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: sbom-${{ github.sha }}
path: |
sbom.cdx.json
vulns.json
--only-fixed)가장 흔한 실패 — 경보 피로
SBOM 스캔을 켜면 처음에 수백 개의 취약점이 쏟아진다. 대부분은 실제로 악용 가능하지 않거나, 우리 사용 방식에서는 영향이 없다. 이걸 그대로 두면 팀이 경보를 무시하기 시작하고, 정작 중요한 것도 묻힌다.
--only-fixed로 패치가 존재하는 취약점만 먼저 처리한다. 즉시 행동 가능한 목록이 된다.SBOM을 실제로 쓰는 순간
취약점 공개 소식이 들어왔을 때의 흐름을 미리 그려 두면, SBOM의 형태와 보관 방식이 자연스럽게 정해진다.
# 시나리오: 특정 패키지의 치명적 취약점 공개
# 1) 보관된 SBOM 전체에서 해당 패키지 검색
grep -l '"name":"vulnerable-lib"' sboms/*.cdx.json
# 2) 버전까지 확인
jq -r '.components[] | select(.name=="vulnerable-lib") | "\(.name) \(.version)"' \
sboms/*.cdx.json | sort | uniq -c
# 3) 영향받는 서비스와 배포 버전 목록화 → 우선순위 결정
# 4) 패치 버전으로 업데이트 후 재빌드·재스캔
# 이 과정이 10분 안에 끝나는 것이 SBOM 을 운영하는 이유다.
라이선스 관점
SBOM은 보안뿐 아니라 라이선스 관리에도 쓰인다. 카피레프트 라이선스가 예상치 못한 경로로 들어오는 경우가 있는데, 전이 의존성까지 포함한 목록이 있으면 사전에 발견할 수 있다. 제품을 배포하는 조직이라면 이쪽이 더 급한 문제일 수도 있다.
자주 묻는 질문
SBOM은 언제 생성하는 게 좋나요?
이미지 빌드 직후가 실무적으로 가장 유용합니다. OS 패키지와 애플리케이션 의존성이 함께 잡히고 배포 산출물과 1:1로 대응하기 때문입니다. 소스 단계 SBOM을 보조로 두면 빌드 과정의 변화를 비교할 수 있습니다.
SPDX와 CycloneDX 중 무엇을 써야 하나요?
둘 다 널리 쓰이므로 사용하는 도구와 제출 대상이 요구하는 형식에 맞추면 됩니다. 생성 비용이 크지 않으므로 두 형식을 함께 만들어 두는 팀도 많습니다.
취약점이 너무 많이 나옵니다. 어떻게 하나요?
수정 버전이 존재하는 항목부터 처리하고, 현재 상태를 기준선으로 고정한 뒤 새로 늘어나는 것만 차단하세요. 무시하기로 한 항목은 사유와 재검토 시점을 기록해 두어야 나중에 판단을 되짚을 수 있습니다.
빌드를 실패시키는 기준은 어떻게 잡나요?
처음에는 치명(critical) 등급이면서 수정 버전이 있는 항목만으로 시작하는 것이 안전합니다. 기준을 너무 엄격하게 잡으면 팀이 스캔을 우회하게 되므로, 대응 가능한 수준에서 시작해 점진적으로 강화하세요.
SBOM을 얼마나 오래 보관해야 하나요?
최소한 해당 버전이 프로덕션에서 운영되는 기간 동안은 유지해야 하며, 사고 조사를 대비해 그보다 길게 두는 것이 좋습니다. 이미지 태그와 SBOM을 대응시켜 두면 사후 추적이 쉬워집니다.
SBOM이 실제로 도움이 되는 순간은 언제인가요?
새 취약점이 공개됐을 때입니다. 보관된 SBOM을 검색하면 영향받는 서비스와 버전을 몇 분 안에 특정할 수 있습니다. 이 목록을 만드는 데 며칠이 걸리는 조직과 몇 분이 걸리는 조직의 차이가 SBOM의 가치입니다.

댓글 0