依赖清单的构成:SBOM 的概念、标准与生成工具

聊到依赖清单,就绕不开一个词——SBOM。很多人第一次听到它,觉得是个高大上的新概念。其实说白了,SBOM 就是一份「软件物料清单」。你想想看,造一辆汽车需要列出所有零件的型号、批次、供应商,软件也一样。

我在项目中遇到过不少团队,代码跑得好好的,突然被安全团队告知某个底层库有高危漏洞。这时候大家才手忙脚乱地去翻依赖。嗯,如果早有一份完整的 SBOM,这种被动局面完全可以避免。

SBOM 到底是什么?

SBOM(Software Bill of Materials)就是一份结构化的清单,记录了软件中包含的所有组件、库、模块及其版本、许可证、依赖关系等信息。它就像你点外卖时的小票——上面清清楚楚写着每个菜品的名字、分量和价格。

核心价值:SBOM 让软件供应链变得透明。你能一眼看出项目里用了什么、从哪里来、有没有已知风险。

我个人习惯把 SBOM 分成三个层次来看:

  • 直接依赖——你主动引入的包,比如在 package.json 里声明的那些。
  • 间接依赖——你引入的包自己又依赖的包,也叫传递依赖。
  • 元数据——许可证信息、版本号、哈希值、来源地址等。

举个例子,你用了一个日志库 log4j,它内部又依赖了 jndi 模块。你的 SBOM 里不仅要记录 log4j,还要把 jndi 也列出来。否则一旦 jndi 出问题,你根本不知道自己的项目是否受影响。

SBOM 标准:SPDX 与 CycloneDX

既然 SBOM 这么重要,那总得有个统一的格式吧?不然你出一份 JSON,我出一份 XML,大家没法互通。目前业界主流的标准有两个:SPDX 和 CycloneDX。

对比维度 SPDX CycloneDX
发起方 Linux 基金会 OWASP
侧重点 许可证合规、版权信息 安全漏洞、依赖关系
格式支持 JSON、YAML、RDF/XML、Tag:Value JSON、XML、Protobuf
漏洞描述 通过外部引用关联 内置漏洞字段,原生支持
适用场景 法律合规、开源审计 安全运维、DevOps 流水线

我个人更倾向于在安全场景中使用 CycloneDX。为什么?因为它原生就支持漏洞描述字段,你生成一份 SBOM 后,可以直接在里面标注每个组件关联的 CVE 编号。SPDX 虽然也能做,但需要额外关联外部数据源,多了一步操作。

我的建议:如果你的团队主要关注许可证合规(比如公司要过开源审计),选 SPDX。如果你们更关心安全漏洞和依赖治理,选 CycloneDX。当然,两个都支持也没问题,工具链上可以同时生成。

依赖清单的生成工具

标准定好了,接下来就是怎么生成 SBOM。市面上有不少工具,我挑几个常用的说说。

1. Syft

Anchore 出品的 Syft 是我用得最多的工具。它支持从容器镜像、文件系统、项目源码中生成 SBOM,输出格式支持 SPDX 和 CycloneDX。

# 从容器镜像生成 SBOM
syft nginx:latest -o cyclonedx-json > sbom.json

# 从项目目录生成
syft ./my-project -o spdx-json > sbom.spdx.json

我记得有一次客户要求提供一份完整的依赖清单,项目有上百个微服务。用 Syft 配合脚本批量跑了一遍,半小时就搞定了。要是手动整理,估计得干到天亮。

2. CycloneDX Generator

OWASP 官方出品的工具,专门为 CycloneDX 标准设计。支持多种语言和包管理器:Maven、npm、pip、NuGet 等。

# 为 Maven 项目生成 CycloneDX SBOM
mvn org.cyclonedx:cyclonedx-maven-plugin:makeBom

# 为 npm 项目生成
npx @cyclonedx/bom --output bom.json

3. Trivy

Trivy 本来是漏洞扫描工具,后来也加入了 SBOM 生成功能。它的优势在于可以一条命令同时完成「生成 SBOM + 扫描漏洞」。

# 生成 SBOM 并扫描漏洞
trivy image --format cyclonedx --output result.cdx.json nginx:latest

避坑指南:我曾经遇到过一个坑——用 Trivy 生成的 SBOM 里,间接依赖的版本号有时会丢失。后来发现是镜像层缓存的问题。建议生成后做一次完整性校验,确保每个组件都有版本号。

4. 各语言生态自带的工具

很多包管理器本身就支持导出依赖清单:

  • npm: npm list --json 可以输出完整的依赖树
  • Maven: mvn dependency:tree 输出依赖树,配合 -DoutputFile 可以保存
  • pip: pip freeze 列出所有已安装的包
  • Go: go list -m all 列出所有模块

但这些工具输出的格式不统一,没法直接当 SBOM 用。我一般把它们作为中间数据,再通过脚本转换成标准的 SPDX 或 CycloneDX 格式。

SBOM 的核心知识体系

下面这张图可以帮你快速理解 SBOM 的构成逻辑:

SBOM 核心知识体系 SBOM 概念与构成 标准规范 生成工具 直接依赖 间接依赖 元数据(版本/许可证) SPDX(许可证合规) CycloneDX(安全漏洞) Syft CycloneDX Generator Trivy SBOM 是软件供应链安全的基础,选对标准、用好工具,才能做到「看得清、管得住」

生成 SBOM 的最佳实践

工具选好了,标准定好了,怎么落地?我总结了几条经验:

  1. 集成到 CI/CD 流水线——每次构建都自动生成 SBOM,而不是等到出问题才去翻。
  2. 版本化管理——SBOM 文件要和代码一起入库,方便回溯历史版本用了哪些依赖。
  3. 定期重新生成——即使代码没变,基础镜像或间接依赖可能已经更新了。
  4. 与漏洞库联动——生成 SBOM 后,自动对接 NVD、GitHub Advisory 等漏洞库做匹配。

一个小技巧:我习惯在 SBOM 文件名里加上构建时间和 Git commit hash,比如 sbom-20250315-abc1234.json。这样排查问题时能精确对应到某个版本的构建产物。