2、SBOM生成实战:用Syft和Trivy动手生成,搞懂JSON与XML的差异

说实话,很多团队把SBOM当成一个「交作业」的东西——CI/CD里加个插件,跑完生成个文件,扔给安全部门就完事了。但如果你真这么干,后面漏洞修复那一步肯定会踩坑。

我个人习惯是,先亲手跑一遍工具,看看生成的SBOM到底长什么样。你只有亲眼见过它的结构,才知道怎么用它来驱动后续的修复流程。这一章我们就来动手,用Syft和Trivy生成真实的SBOM,然后掰开揉碎看看JSON和XML这两种格式到底差在哪。

2.1 选对工具:Syft vs Trivy,我什么时候用哪个?

市面上能生成SBOM的工具不少,但最主流的就两个:Anchore家的Syft,和Aqua Security家的Trivy。我在项目里两个都用过,简单说说我的感受。

特性 Syft Trivy
核心定位 专注SBOM生成,轻量、快速 全能型:SBOM + 漏洞扫描 + 密钥检测
支持格式 SPDX、CycloneDX、Syft自有的JSON SPDX、CycloneDX(输出时指定)
扫描速度 极快,适合CI流水线 稍慢(因为同时做漏洞匹配)
我的使用场景 纯生成SBOM,不关心漏洞 生成SBOM后直接看漏洞
我的建议:如果你只需要生成SBOM交给第三方或合规审计,用Syft就够了。如果你生成SBOM后马上要修漏洞,直接用Trivy一步到位,省得再跑一遍扫描。

2.2 动手实战:用Syft生成第一个SBOM

我们先从Syft开始。安装很简单,一行命令搞定:

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

# 验证安装
syft version

然后找一个你本地的Docker镜像,比如我常用的 nginx:alpine:

syft nginx:alpine -o spdx-json > sbom-nginx-spdx.json

跑完之后,你会得到一个JSON文件。打开看看,结构大概是这样的:

{
  "spdxVersion": "SPDX-2.3",
  "dataLicense": "CC0-1.0",
  "SPDXID": "SPDXRef-DOCUMENT",
  "name": "nginx:alpine",
  "packages": [
    {
      "name": "libcrypto1.1",
      "versionInfo": "1.1.1k-r0",
      "SPDXID": "SPDXRef-1",
      "supplier": "Organization: Alpine Linux",
      "licenseConcluded": "OpenSSL"
    },
    {
      "name": "nginx",
      "versionInfo": "1.21.6-r0",
      "SPDXID": "SPDXRef-2",
      "supplier": "Organization: Alpine Linux",
      "licenseConcluded": "BSD-2-Clause"
    }
  ]
}

嗯,这里要注意:packages数组里每个元素就是一个组件。我刚开始看的时候觉得挺直观的,但后来发现一个问题——SPDX格式的JSON里,依赖关系是单独放在relationships字段里的,不是嵌套在包里面。这意味着你要解析依赖树,得把两个字段关联起来看。

2.3 换一种格式:生成CycloneDX的XML

SPDX是Linux基金会推的,CycloneDX则是OWASP推的。很多安全工具更倾向于CycloneDX,尤其是XML版本。我们来生成一个看看:

syft nginx:alpine -o cyclonedx-xml > sbom-nginx-cyclonedx.xml

打开XML文件,你会发现结构完全不同:

<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.4" serialNumber="urn:uuid:..." version="1">
    <components>
        <component type="library">
            <name>libcrypto1.1</name>
            <version>1.1.1k-r0</version>
            <licenses>
                <license>
                    <id>OpenSSL</id>
                </license>
            </licenses>
        </component>
        <component type="library">
            <name>nginx</name>
            <version>1.21.6-r0</version>
            <licenses>
                <license>
                    <id>BSD-2-Clause</id>
                </license>
            </licenses>
        </component>
    </components>
    <dependencies>
        <dependency ref="pkg:alpine/nginx@1.21.6-r0">
            <dependency ref="pkg:alpine/libcrypto1.1@1.1.1k-r0"/>
        </dependency>
    </dependencies>
</bom>
关键区别:CycloneDX的XML把组件和依赖关系分成了<components>和<dependencies>两个独立的部分。而SPDX的JSON里,依赖关系是通过relationships字段表达的,每个relationship有一个spdxElementId和relatedSpdxElement。

2.4 JSON vs XML:到底差在哪?

我经常被问到:「到底该用JSON还是XML?」说实话,没有绝对答案。但根据我踩过的坑,可以给你几个判断依据:

对比维度 JSON XML
可读性 人类友好,结构紧凑 标签多,文件体积大
解析性能 快,原生JavaScript/Python支持 慢,需要XML解析器
Schema验证 较弱(JSON Schema不够成熟) 强(XSD、DTD成熟)
工具生态 现代工具链首选 传统企业工具链常用
我的推荐 CI/CD流水线、微服务场景 合规审计、与旧系统对接
我曾经踩过的坑:有一次我把SPDX JSON格式的SBOM直接喂给一个老旧的漏洞管理平台,结果它解析不了。折腾了半天才发现,那个平台只支持CycloneDX XML格式。所以,在选格式之前,先确认下游工具支持什么,不然生成得再漂亮也是白搭。

2.5 用Trivy一步到位:生成SBOM + 漏洞扫描

如果你像我一样懒,不想先跑Syft再跑漏洞扫描,那直接用Trivy吧。它可以在生成SBOM的同时告诉你哪些组件有漏洞:

# 生成CycloneDX格式的SBOM,同时显示漏洞
trivy image --format cyclonedx --output sbom-trivy.json nginx:alpine

# 如果你只想看漏洞,不想输出SBOM文件
trivy image nginx:alpine

Trivy的输出里,SBOM部分和Syft差不多,但多了一个vulnerabilities字段。我个人习惯是:在CI/CD里用Trivy生成SBOM并直接做漏洞阻断,这样一次扫描解决两个问题。

2.6 核心知识图谱:SBOM生成流程

下面这张图总结了从镜像到SBOM再到漏洞修复的完整链路,你可以把它贴在工位旁边:

SBOM生成与格式选择流程 容器镜像 / 代码仓库 选择工具:Syft / Trivy Syft:纯SBOM生成 Trivy:SBOM + 漏洞 SPDX JSON / CycloneDX XML CycloneDX JSON + 漏洞列表 漏洞修复 / 合规审计 格式选错 = 下游工具无法解析 先确认目标平台支持的格式

2.7 避坑指南:我踩过的三个坑

  1. 格式混用导致解析失败
    我曾经把SPDX JSON格式的SBOM直接传给一个只认CycloneDX的工具,结果它报了一堆莫名其妙的错误。后来我学乖了:生成之前先问清楚下游要什么格式。
  2. 依赖关系不完整
    有一次我用Syft扫描一个Java应用,生成的SBOM里只有直接依赖,没有传递依赖。后来发现是因为我没有指定--scope all-layers参数。默认情况下,Syft只扫描最后一层,如果你需要完整的依赖树,记得加这个参数。
  3. 版本号不一致
    Trivy和Syft对同一个组件的版本号解析方式可能不同。比如某个Alpine包,Syft显示的是1.1.1k-r0,Trivy可能显示1.1.1k。这会导致漏洞匹配时出现偏差。我的做法是:统一用一个工具生成SBOM和做漏洞扫描,避免版本号打架。
一个小技巧:如果你不确定下游工具支持什么格式,可以生成两种格式的SBOM——SPDX JSON和CycloneDX XML都留着。反正文件不大,多一个备份总比到时候抓瞎强。