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 的完整流程,我把它总结为「三步走」:
流程其实不复杂:先通过包管理器(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参数。