3. SBOM 生成工具:从选型到实战

聊到 SBOM 的落地,第一个绕不开的问题就是:用什么工具生成?

市面上工具不少,但真正能打、社区活跃、生态好的,其实就几个流派。我个人这些年折腾下来,主力工具基本固定在 SPDX 官方工具链、CycloneDX 工具链,以及 Syft + Trivy 这个黄金组合上。

今天我就把这几个工具怎么用、各自擅长什么、有哪些坑,掰开揉碎了讲一遍。

核心观点:没有万能工具。SPDX 适合合规审计,CycloneDX 适合集成到 CI/CD,Syft + Trivy 适合快速扫描和容器场景。选对工具,事半功倍。

SBOM 生成工具生态全景 SPDX 工具链 • 官方工具 (Java) • 格式验证器 • 文档生成器 • 许可证匹配器 CycloneDX 工具链 • 多语言支持 • 依赖图生成 • 漏洞关联 • CI/CD 集成 Syft + Trivy • 容器镜像扫描 • 文件系统扫描 • 漏洞检测 • 快速轻量 输出格式:JSON / XML / Tag:Value / RDF / XLSX 合规审计 SPDX 主导 CI/CD 集成 CycloneDX 主导 容器安全 Syft + Trivy 主导

3.1 SPDX 工具链:合规审计的基石

SPDX 工具链是 Linux 基金会维护的官方工具集。说白了,它是为合规审计而生的。

我记得第一次用 SPDX 工具,是在一个金融客户的项目里。对方要求所有第三方组件必须提供 SPDX 格式的 SBOM,否则不予上线。那时候我才真正意识到,SPDX 在合规领域的统治力有多强。

3.1.1 核心工具一览

工具名称 功能 适用场景
spdx-tools SPDX 文档生成、验证、转换 Java 项目、合规审计
spdx-sbom-generator 自动生成 SPDX SBOM 多语言项目
spdx-license-matcher 许可证匹配与识别 许可证合规检查
spdx-validator SPDX 文档格式验证 质量门禁

3.1.2 实战:生成 SPDX SBOM

以 Java 项目为例,用 spdx-tools 生成 SBOM 其实不复杂:

# 下载 spdx-tools
wget https://github.com/spdx/tools/releases/download/v2.2.6/spdx-tools-2.2.6.jar

# 生成 SPDX 文档(Tag:Value 格式)
java -jar spdx-tools-2.2.6.jar \
  -in /path/to/project \
  -out output.spdx \
  -format tagvalue

# 验证生成的 SPDX 文档
java -jar spdx-tools-2.2.6.jar \
  -verify output.spdx

我的经验:SPDX 的 Tag:Value 格式可读性最好,适合人工审查。但如果你要集成到自动化流水线,建议用 JSON 格式,解析起来更方便。

3.1.3 避坑指南

我曾经在生成 SPDX 文档时,遇到过一个诡异的错误——文档验证不通过,但怎么看格式都没问题。后来发现,是项目路径里包含了中文目录名,spdx-tools 对 Unicode 支持不够好。

所以,项目路径尽量用纯英文,这是个小坑,但能省你半天时间。

3.2 CycloneDX 工具链:CI/CD 集成的利器

如果说 SPDX 是合规审计的「老大哥」,那 CycloneDX 就是 CI/CD 集成的「尖刀」。它由 OWASP 社区维护,天生就带着安全基因。

我个人更喜欢 CycloneDX 的一点是:它原生支持依赖图。你想想看,一个 SBOM 如果只列出组件清单,却不告诉你组件之间的依赖关系,那价值就大打折扣了。

3.2.1 核心工具一览

工具名称 功能 适用场景
cyclonedx-maven-plugin Maven 项目 SBOM 生成 Java/Maven 项目
cyclonedx-gradle-plugin Gradle 项目 SBOM 生成 Java/Gradle 项目
cyclonedx-npm Node.js 项目 SBOM 生成 JavaScript/TypeScript
cyclonedx-python Python 项目 SBOM 生成 Python 项目
cyclonedx-cli 命令行工具,格式转换 通用场景

3.2.2 实战:在 Maven 项目中集成 CycloneDX

我建议直接在 pom.xml 里配置插件,这样每次构建都会自动生成 SBOM:

<plugin>
  <groupId>org.cyclonedx</groupId>
  <artifactId>cyclonedx-maven-plugin</artifactId>
  <version>2.7.9</version>
  <executions>
    <execution>
      <phase>package</phase>
      <goals>
        <goal>makeAggregateBom</goal>
      </goals>
    </execution>
  </executions>
  <configuration>
    <projectType>application</projectType>
    <schemaVersion>1.4</schemaVersion>
    <includeBomSerialNumber>true</includeBomSerialNumber>
    <includeCompileScope>true</includeCompileScope>
    <includeProvidedScope>true</includeProvidedScope>
    <includeRuntimeScope>true</includeRuntimeScope>
    <includeSystemScope>false</includeSystemScope>
    <includeTestScope>false</includeTestScope>
    <outputFormat>json</outputFormat>
    <outputName>bom</outputName>
  </configuration>
</plugin>

执行 mvn package 后,在 target/ 目录下会生成 bom.json 文件。这就是你的 CycloneDX 格式 SBOM。

注意:默认配置下,test scope 的依赖不会包含在 SBOM 中。如果你的测试框架有安全风险(比如 JUnit 的某些旧版本),记得手动调整 includeTestScope 为 true。

3.2.3 格式转换:CycloneDX ↔ SPDX

实际项目中,你可能会遇到「甲方要 SPDX,但团队用 CycloneDX」的情况。别慌,cyclonedx-cli 可以帮你做格式转换:

# CycloneDX JSON 转 SPDX JSON
cyclonedx-cli convert \
  --input-file bom.json \
  --output-file bom.spdx.json \
  --output-format spdxjson

嗯,这里要注意:转换后的 SPDX 文档可能丢失一些 CycloneDX 特有的字段(比如依赖图信息)。所以,尽量保持原生格式,转换只是权宜之计。

3.3 Syft 与 Trivy:容器时代的黄金搭档

聊完两个「老牌」工具链,咱们来看看新生代的力量——Syft 和 Trivy。

这两个工具都是 Anchore 公司开源的,一个负责生成 SBOM(Syft),一个负责漏洞扫描(Trivy)。配合起来用,效果 1+1 > 2。

我为什么喜欢它们?快,真的快。 扫描一个几百 MB 的容器镜像,Syft 几秒钟就能生成 SBOM,Trivy 也只要十几秒就能完成漏洞检测。

3.3.1 Syft:容器镜像的 SBOM 生成器

Syft 支持从容器镜像、文件系统、OCI 目录等多种来源生成 SBOM。安装也简单:

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

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

# 生成文件系统的 SBOM
syft dir:/path/to/project -o spdx-json > project-sbom.spdx.json

Syft 默认输出 CycloneDX 格式,但你可以通过 -o 参数指定 SPDX、Syft 自己的 JSON 格式等。

我的习惯:在 CI/CD 流水线里,我会先用 Syft 生成 SBOM,然后把 SBOM 传给 Trivy 做漏洞扫描。这样,SBOM 和漏洞报告是关联的,审计时一目了然。

3.3.2 Trivy:不只是漏洞扫描

很多人以为 Trivy 只是个漏洞扫描器,其实它也能生成 SBOM。不过,我更推荐用 Syft 生成 SBOM,用 Trivy 做漏洞检测——各司其职。

# 安装 Trivy
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh

# 扫描容器镜像漏洞
trivy image nginx:latest

# 扫描 SBOM 文件中的漏洞
trivy sbom nginx-sbom.json

# 生成 SBOM(Trivy 也支持)
trivy image --format cyclonedx nginx:latest > nginx-sbom-trivy.json

Trivy 的 trivy sbom 命令特别实用——你不需要重新扫描镜像,直接拿已有的 SBOM 文件就能做漏洞匹配。这在离线环境或者镜像已经删除的场景下,简直是救命功能。

3.3.3 实战:Syft + Trivy 集成到 Jenkins

我曾在 Jenkins 流水线里这样配置:

stage('SBOM 生成') {
  steps {
    script {
      // 生成 SBOM
      sh "syft ${DOCKER_IMAGE}:${BUILD_TAG} -o json > sbom.json"
      // 存档 SBOM
      archiveArtifacts artifacts: 'sbom.json'
    }
  }
}

stage('漏洞扫描') {
  steps {
    script {
      // 基于 SBOM 做漏洞扫描
      sh "trivy sbom sbom.json --format json --output trivy-report.json"
      // 如果存在高危漏洞,构建失败
      sh "trivy sbom sbom.json --exit-code 1 --severity CRITICAL"
    }
  }
}

注意:Trivy 的 --exit-code 参数要谨慎使用。如果设得太严(比如所有 CRITICAL 漏洞都阻断构建),开发团队可能会抱怨。我建议先设为 --exit-code 0 只做报告,等团队适应了再逐步收紧。

3.4 生成策略与最佳实践

工具讲完了,但光有工具还不够。你得有一套生成策略,否则 SBOM 就是一盘散沙。

3.4.1 生成频率:什么时候生成?

场景 生成频率 说明
CI/CD 构建 每次构建 确保 SBOM 与制品版本一一对应
依赖更新 依赖变更时 比如升级了某个库,重新生成
定期审计 每周/每月 对存量系统做周期性扫描
发布前 每次发布 作为发布工单的必选项

3.4.2 生成范围:包含什么?

我见过不少团队,生成的 SBOM 只包含「直接依赖」,忽略了「传递依赖」。这其实是个大坑。

举个例子:你的项目直接依赖了 Log4j 2.14.0,但 Log4j 又依赖了 JNDI 库。如果只列直接依赖,Log4Shell 漏洞(CVE-2021-44228)根本不会出现在你的 SBOM 里。你想想看,这 SBOM 还有什么用?

所以,一定要包含完整的依赖树。具体来说:

  • 直接依赖:项目显式声明的依赖
  • 传递依赖:依赖的依赖,递归解析
  • 系统组件:操作系统包、基础镜像层
  • 构建工具:Maven、Gradle、npm 等工具本身

3.4.3 最佳实践清单

  1. 统一格式:团队内统一使用 CycloneDX 或 SPDX,不要混用
  2. 版本锁定:SBOM 文件名包含制品版本号,比如 myapp-v1.2.3-bom.json
  3. 签名验证:对 SBOM 文件做 GPG 签名,防止篡改
  4. 存储归档:SBOM 随制品一起归档,存到 OCI Registry 或 S3
  5. 自动化门禁:SBOM 生成失败 = 构建失败,强制团队执行
  6. 定期刷新:即使代码没变,也定期重新生成 SBOM(因为漏洞库会更新)

一句话总结:SBOM 生成不是一次性工作,而是持续的过程。工具选对、策略定好、自动化跑起来,你就能在供应链安全上迈出坚实的一步。