2026년 하루 평균 보고된 CVE가 전년 대비 131% 증가했습니다.

SBOM

Docker SBOM: 생성 방법과 놓치는 것

Docker 이미지의 SBOM은 명령 하나로 얻을 수 있습니다. 빌드 시점에는 BuildKit으로, 빌드 후에는 Docker Scout나 Syft로 생성합니다. 각 도구는 이미지에서 찾은 패키지 메타데이터를 읽습니다. 메타데이터가 미치는 범위 안에서는 이미지에 무엇이 들어 있는지에 대한 올바른 기록입니다. 하지만 이미지가 어떻게 빌드되었는지에 대한 기록은 아닙니다.

업데이트

Docker 이미지의 SBOM을 생성하는 방법

흔히 쓰는 방법은 세 가지입니다. BuildKit은 이미지를 빌드하면서 SBOM을 작성하고 이를 증명(attestation)으로 이미지에 첨부할 수 있습니다. Docker Scout와 Syft는 로컬이나 레지스트리에 이미 있는 이미지에서 SBOM을 생성합니다.

빌드 시점에 BuildKit으로

--sbom=true 또는 긴 형식인 --attest type=sbom을 docker buildx build에 추가하세요. BuildKit은 결과물에 Syft 스캐너 플러그인을 실행하고, SBOM을 SPDX JSON 문서로 이미지에 첨부합니다. 아무것도 푸시하기 전에 SBOM을 살펴보려면 local 익스포터로 빌드하세요.

# Build the image, attach an SBOM attestation and push it
docker buildx build --sbom=true -t registry.example.com/app:1.0 --push .

# The long form of the same option
docker buildx build --attest type=sbom -t registry.example.com/app:1.0 --push .

# Check the SBOM before pushing: write the build result to ./out
docker buildx build --sbom=true --output type=local,dest=out .
# out/sbom.spdx.json is the SBOM

이미지에 첨부된 SBOM 읽기

docker buildx imagetools inspect는 레지스트리에서 증명을 다시 읽어 옵니다. SBOM 관련 정보는 모두 Go 템플릿의 .SBOM 아래에 있습니다.

# Print the SPDX document attached to the image
docker buildx imagetools inspect registry.example.com/app:1.0 \
  --format "{{ json .SBOM.SPDX }}"

# List each package and its version
docker buildx imagetools inspect registry.example.com/app:1.0 \
  --format "{{ range .SBOM.SPDX.packages }}{{ .name }}@{{ .versionInfo }}{{ println }}{{ end }}"

기존 이미지에서 Docker Scout로

docker scout sbom은 이미지에 SBOM 증명이 있으면 그것을 사용합니다. 없으면 이미지 내용을 직접 인덱싱합니다. 기본적으로 JSON을 출력하며, --output으로 파일에 저장합니다.

# List the packages in an image
docker scout sbom --format list registry.example.com/app:1.0

# Write an SPDX or CycloneDX SBOM to a file
docker scout sbom --format spdx --output app.spdx.json registry.example.com/app:1.0
docker scout sbom --format cyclonedx --output app.cdx.json registry.example.com/app:1.0

기존 이미지에서 Syft로

Syft는 BuildKit 플러그인의 기반이 되는 오픈 소스 스캐너입니다. 이미지를 지정하고 -o로 형식을 선택하세요. =file을 추가하면 형식마다 별도 파일로 저장합니다.

# CycloneDX JSON to the terminal
syft registry.example.com/app:1.0 -o cyclonedx-json

# Both formats, each to its own file
syft registry.example.com/app:1.0 -o spdx-json=app.spdx.json -o cyclonedx-json=app.cdx.json

SPDX와 CycloneDX 중 무엇을 쓸까?

BuildKit 증명은 항상 SPDX입니다. Docker Scout는 SPDX 또는 CycloneDX를 작성하고, Syft는 다른 형식과 함께 둘 다 작성합니다. 두 표준 모두 패키지, 버전, 라이선스, 패키지 URL을 나열하므로, 보통은 다음에 SBOM을 읽을 도구에 따라 선택이 갈립니다. 차이점은 CycloneDX vs SPDX를 참고하세요.

이미지 스캐너가 SBOM을 만드는 방식

이 도구들은 모두 같은 방식으로 작동합니다. 이미지 레이어를 풀고, 찾은 패키지 메타데이터를 읽습니다. 운영체제의 패키지 데이터베이스와 언어별 패키지 관리자가 남긴 메타데이터가 그 대상입니다. 식별할 수 있는 모든 패키지가 SBOM 항목이 되며, 베이스 이미지에서 온 패키지도 포함됩니다.

빌드 단계와 빌드 컨텍스트 포함하기

기본적으로 BuildKit은 최종 단계만 스캔합니다. 멀티 스테이지 빌드에서는 이전 단계에서 사용한 컴파일러, 빌드 도구, 패키지가 최종 이미지에 없으므로 SBOM에도 나타나지 않습니다. Docker의 SBOM 증명 문서는 스캔 범위를 넓히는 두 가지 빌드 인수를 설명합니다. BUILDKIT_SBOM_SCAN_STAGE는 단계를 추가하고, BUILDKIT_SBOM_SCAN_CONTEXT는 빌드 컨텍스트를 추가합니다.

# syntax=docker/dockerfile:1
# Scan the build context as well as the image
ARG BUILDKIT_SBOM_SCAN_CONTEXT=true

FROM golang:1.25 AS build
# Include this build stage in the SBOM
ARG BUILDKIT_SBOM_SCAN_STAGE=true
WORKDIR /src
COPY . .
RUN go build -o /out/app .

FROM scratch
COPY --from=build /out/app /app
ENTRYPOINT ["/app"]

두 인수 모두 Dockerfile에서 ARG로 선언해야 합니다. --build-arg로만 설정하면 효과가 없지만, Dockerfile이 선언한 값을 --build-arg로 재정의할 수는 있습니다. local 익스포터를 사용하면 sbom-build.spdx.json처럼 스캔된 각 단계가 sbom.spdx.json 옆에 별도 파일로 저장됩니다.

스캔이 여전히 놓치는 것

단계 스캔으로 빈틈 하나는 메워집니다. 그래도 스캐너는 여전히 파일을 읽으며, 빌드가 사용하는 것 중 일부는 파일을 남기지 않거나 스캐너가 식별할 수 있는 파일을 남기지 않습니다.

  • 패키지 메타데이터가 없는 파일. curl로 내려받은 바이너리, RUN 단계에서 압축을 푼 tarball, 복사해 넣은 벤더링 코드가 그렇습니다. Go 바이너리처럼 스캐너가 읽을 수 있는 빌드 정보를 담은 바이너리도 있지만, 그렇지 않은 바이너리가 많습니다.
  • 빌드가 내려받고 삭제한 것. RUN 단계가 같은 레이어 안에서 가져와 사용하고 제거한 패키지나 설치 프로그램은, 최종 단계든 아니든 어떤 스캐너도 읽을 수 있는 흔적을 파일 시스템에 남기지 않습니다.
  • 어떻게 들어왔는지. 스캔으로 만든 SBOM은 무엇이 있는지를 나열합니다. 패키지가 어떤 레지스트리나 호스트에서 왔는지, 어떤 단계가 가져왔는지는 알려 주지 않습니다.
  • 표시하지 않은 단계. 단계 스캔은 단계별 또는 전역으로 명시적으로 켜야 합니다. 인수가 없는 단계는 여전히 제외됩니다.
  • 스캔 설정. 무엇이 나타나는지는 스캐너를 어떻게 실행하느냐에 달려 있습니다. 어떤 프로젝트에서는 Syft 플래그 하나에 따라 npm 패키지가 결과에 아예 나타나는지 여부가 결정됩니다.

Yocto 빌드에도 같은 문제가 있습니다. SBOM 스캐너가 Yocto 빌드의 내용을 놓치는 이유를 참고하세요.

왜 중요한가

손상된 빌드 도구나 베이스 이미지는 최종 이미지에 나타나지 않더라도 출시하는 제품에 영향을 줍니다. 그리고 메타데이터가 없는 바이너리에는 어떤 SBOM 항목도 가리키지 않는 취약점이 있을 수 있습니다. 보안 권고가 발표되면 이미지 안에 무엇이 있는지뿐 아니라 이미지를 빌드하는 데 무엇이 들어갔는지도 알아야 합니다.

빌드도 함께 기록하세요

이미지 SBOM은 이미지 안에 무엇이 있는지에 답합니다. 빌드 기록은 빌드가 무엇을 가져왔는지에 답합니다. 가져온 베이스 이미지와 각 단계가 가져온 패키지가 여기에 해당하며, 어떤 잠금 파일에도 나열되지 않은 숨은 종속성도 포함됩니다. CRACI는 GitHub Actions 작업이 실행되는 동안 러너에 있는 패키지 인식 프록시를 통해 이를 기록하며, API는 해당 작업의 SBOM, 네트워크 추적, 출처 증명을 반환합니다. 출시하는 것에는 이미지 SBOM을, 그것이 어떻게 만들어졌는지에는 빌드 기록을 활용하세요.

SBOM 도구 비교, 빌드 시점 SBOM, GitHub Actions에서 SBOM을 생성하는 방법을 참고하세요.

Docker SBOM: 자주 묻는 질문

Docker 이미지의 SBOM은 어떻게 생성하나요?

흔히 쓰는 방법은 세 가지입니다. --sbom=true를 docker buildx build에 추가하면 BuildKit이 SPDX SBOM을 이미지에 첨부합니다. 기존 이미지에 docker scout sbom을 실행할 수도 있습니다. 또는 syft IMAGE -o cyclonedx-json처럼 Syft를 실행하세요. 세 가지 모두 이미지에서 찾은 패키지를 읽습니다.

SBOM 스캐너란 무엇인가요?

소스 디렉터리, 잠금 파일, 컨테이너 이미지 같은 파일을 읽고, 식별할 수 있는 패키지의 SBOM을 작성하는 도구입니다. Syft, Trivy, Docker Scout, BuildKit의 Syft 플러그인이 SBOM 스캐너입니다. 이 도구들은 눈앞에 있는 파일을 설명할 뿐, 그 파일을 만든 빌드를 설명하지는 않습니다.

Docker는 어떤 SBOM 형식을 생성하나요?

BuildKit 증명은 이미지에 첨부되는 SPDX JSON 문서입니다. Docker Scout는 패키지 목록을 출력하거나 SPDX 또는 CycloneDX를 작성할 수 있습니다. Syft는 둘 다, 그리고 그 밖의 형식도 작성합니다. SBOM을 사용할 도구가 기대하는 형식을 선택하세요.

스캐너는 Docker 이미지에서 SBOM을 어떻게 만드나요?

이미지 레이어를 풀고 찾은 패키지 메타데이터를 읽습니다. 운영체제의 패키지 데이터베이스와, 설치된 npm 또는 Python 패키지 메타데이터 같은 언어별 패키지 파일이 대상입니다. 식별할 수 있는 각 패키지가 SBOM의 항목이 됩니다.

이미지 SBOM이 구성 요소를 놓치는 이유는 무엇인가요?

읽는 파일에서 식별할 수 있는 것만 나열할 수 있기 때문입니다. curl로 내려받은 바이너리에는 패키지 메타데이터가 없고, 빌드 단계가 내려받았다가 삭제한 패키지는 읽을 흔적을 남기지 않으며, 단계 스캔을 켜지 않으면 이전 빌드 단계는 제외됩니다.

이미지 SBOM은 여전히 유용한가요?

네. 베이스 이미지의 패키지를 포함해, 출시하는 이미지 안에 무엇이 있는지에 대한 올바른 기록입니다. 다만 이미지가 어떻게 빌드되었는지는 설명하지 않으므로, 그것이 중요할 때는 빌드 기록과 함께 사용하세요.

이미지 빌드가 가져온 것을 확인하세요

CRACI에서 이미지 빌드 하나를 실행하고, 가져온 베이스 이미지와 패키지의 기록을 스캐너가 만든 이미지 SBOM과 나란히 놓아 보세요.

데모 예약