SBOM 基础:定义、格式与生成工具

聊供应链安全,SBOM 是绕不开的第一个门槛。说白了,SBOM 就是一份「软件物料清单」——你用的组件、版本、依赖关系,全得列清楚。我刚开始接触这个领域时,觉得不就是个清单嘛,后来踩了坑才明白,SBOM 的格式和生成方式,直接决定了后续漏洞追踪能不能跑通。

SBOM 到底是什么?

SBOM(Software Bill of Materials)记录了一个软件产品中所有组件的元数据。包括组件名称、版本号、供应商、许可证、依赖关系等。你可以把它想象成一道菜的配料表——没有配料表,出了问题你都不知道该找谁。

我在项目中遇到过一家客户,他们的应用用了 200 多个开源组件,但没有任何记录。结果 Log4j 漏洞爆发时,光排查就花了两周。嗯,从那以后,SBOM 就成了我所有项目的强制要求。

核心要点:SBOM 不是可选项,而是供应链安全的基础设施。没有 SBOM,漏洞追踪就是盲人摸象。

主流 SBOM 格式:SPDX 与 CycloneDX

目前业界最通用的两种格式是 SPDX 和 CycloneDX。它们各有侧重,我建议根据场景选择。

特性 SPDX CycloneDX
发起方 Linux 基金会 OWASP
核心关注点 许可证合规、版权信息 安全漏洞、依赖关系
数据格式 JSON、YAML、RDF/XML、Tag:Value JSON、XML
漏洞描述 通过外部引用关联 内置漏洞字段(Vulnerability)
适用场景 法律合规、审计 安全运维、漏洞管理

我个人习惯:做许可证审计时用 SPDX,做漏洞追踪时用 CycloneDX。你想想看,SPDX 对许可证的描述非常细致,甚至能精确到每个文件的版权声明;而 CycloneDX 原生支持漏洞信息,直接就能把 CVE 挂上去。

小技巧:如果团队刚起步,我建议从 CycloneDX 开始。它的 JSON 结构更简洁,工具链也更成熟。等流程跑通了,再考虑 SPDX 的合规需求。

生成工具:syft 与 cdxgen

有了格式,还得有工具把 SBOM 生成出来。我常用的两个工具是 syft 和 cdxgen,它们各有千秋。

syft:快速、精准的 SBOM 生成器

syft 是 Anchore 开源的,主打一个「快」。它能扫描容器镜像、文件系统、源码目录,直接输出 SPDX 或 CycloneDX 格式。我在 CI/CD 流水线里用得最多。

# 安装 syft
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin

# 扫描容器镜像,输出 CycloneDX JSON
syft nginx:latest -o cyclonedx-json > nginx-sbom.json

# 扫描本地项目目录
syft ./my-project -o spdx-json > my-project-sbom.json

syft 的优点是依赖解析非常准。我记得有一次扫描一个 Java 项目,它连传递依赖的传递依赖都挖出来了,省了我不少手动排查的时间。

cdxgen:专为 CycloneDX 而生

cdxgen 是 OWASP CycloneDX 社区维护的工具,专注于生成 CycloneDX 格式的 SBOM。它支持的语言和包管理器非常全,从 npm、Maven 到 Go modules,几乎全覆盖。

# 安装 cdxgen
npm install -g @cyclonedx/cdxgen

# 生成 SBOM
cdxgen -o sbom.json

# 指定输出格式(默认 JSON)
cdxgen -o sbom.xml -t xml

cdxgen 有个很实用的功能:它能自动识别项目类型。你只要在项目根目录跑一下,它就能根据 package.json、pom.xml 等文件推断出该用什么解析器。嗯,这个特性在微服务架构里特别省事。

避坑指南:我曾经在生成 SBOM 时忽略了一个细节——工具默认只扫描直接依赖,不会自动解析传递依赖。如果你用的是 syft,记得加 --scope all-layers 参数;cdxgen 则默认会解析传递依赖,但需要确保网络能访问到包仓库。

工具对比与选型建议

特性 syft cdxgen
输出格式 SPDX、CycloneDX CycloneDX(JSON/XML)
扫描对象 容器镜像、文件系统、源码 源码目录、包管理器
传递依赖 需手动指定 默认解析
性能 极快(Go 编写) 中等(Node.js 编写)
适用场景 CI/CD 集成、容器安全 开发阶段、多语言项目

我个人建议:如果团队以容器化部署为主,优先用 syft;如果项目是传统微服务、多语言混合,cdxgen 更灵活。当然,两个都装上也不冲突——我经常先用 syft 扫一遍,再用 cdxgen 做交叉验证。

SBOM 生成流程

下面这张图展示了从项目源码到 SBOM 的完整流程,我把它总结为「三步走」:

SBOM 生成流程 SBOM 生成流程 项目源码 包管理器解析 依赖树构建 工具选择 syft cdxgen SBOM 输出 输出格式 SPDX / CycloneDX JSON / XML

流程其实不复杂:先通过包管理器(npm、Maven、Go modules 等)解析出依赖树,然后选择合适的工具生成 SBOM。我建议把这一步集成到 CI/CD 里,每次构建都自动生成一份 SBOM,存档到统一平台。

实战:生成一份完整的 SBOM

我们来实际操作一下。假设你有一个 Node.js 项目,用 Express 框架。我会用 cdxgen 生成 SBOM,然后用 syft 做验证。

# 第一步:用 cdxgen 生成 SBOM
cd /path/to/your/node-project
cdxgen -o sbom-cyclonedx.json

# 第二步:用 syft 扫描同一目录,交叉验证
syft dir:. -o cyclonedx-json > sbom-syft.json

# 第三步:对比两份 SBOM 的组件数量
jq '.components | length' sbom-cyclonedx.json
jq '.artifacts | length' sbom-syft.json

嗯,这里要注意:两份 SBOM 的组件数量可能不完全一致。为什么?因为 syft 和 cdxgen 的依赖解析策略不同。syft 更激进,会把系统级的库也扫进来;cdxgen 则更聚焦于项目本身的依赖。我一般以 cdxgen 为准,再用 syft 做补充。

经验之谈:生成 SBOM 后,别急着扔一边。我建议用 grype(Anchore 的漏洞扫描工具)直接对 SBOM 做漏洞匹配,一步到位。命令很简单:grype sbom:sbom-cyclonedx.json。

常见问题与避坑

  • SBOM 太大怎么办?——大型项目可能有上千个组件。我建议按模块拆分,每个微服务生成独立的 SBOM,别一股脑全塞一起。
  • 私有包怎么处理?——syft 和 cdxgen 都支持通过配置文件指定私有仓库地址。记得在环境变量里配好认证信息。
  • 版本号对不上?——我曾经遇到过,同一个组件在 SBOM 里显示 1.2.3,但实际用的是 1.2.4。原因是工具缓存了旧数据。解决办法:每次生成前清缓存,或者用 --force 参数。