1. SBOM 基础概念:从“软件成分表”说起

大家好,我是你们这门课的老朋友。今天咱们聊点最基础的——SBOM 到底是什么玩意儿。

说白了,SBOM 就是一份“软件成分表”。你想想看,你去超市买一包薯片,包装袋背面会印着配料表:土豆、植物油、食用盐……SBOM 干的就是同样的事。它把软件里用了哪些组件、什么版本、从哪里来的,清清楚楚列出来。

我最早接触 SBOM 是在 2019 年,当时帮一家金融客户做供应链安全审计。他们买了一套第三方支付中间件,结果出了漏洞,排查了整整两周才发现是某个老旧的日志库惹的祸。那时候我就想:要是当时有份 SBOM,十分钟就能定位问题。

一句话定义:SBOM(Software Bill of Materials,软件物料清单)是一份结构化、机器可读的清单,记录了构建软件所用的所有组件、依赖关系及其元数据。

1.1 SBOM 的组成要素

一份完整的 SBOM 到底该包含哪些东西?我习惯把它拆成四个核心部分:

  • 组件标识:每个组件的唯一名字,比如 openssl-1.1.1。最好带上供应商、产品名和版本号。
  • 依赖关系:谁依赖谁,是直接依赖还是传递依赖。这个在漏洞分析时特别关键——一个间接依赖的漏洞往往容易被忽略。
  • 元数据:许可证信息、哈希值、发布时间、作者等。哈希值可以用来验证组件有没有被篡改。
  • 生命周期状态:这个组件还在维护吗?有没有 EOL(生命周期结束)?我见过不少项目还在用 2015 年就停更的库,想想都后怕。

我的习惯:每次生成 SBOM 后,我会先检查“生命周期状态”这一列。如果发现 EOL 组件,直接标红处理——这是漏洞优先级排序的第一步。

举个例子,一个典型的 Node.js 项目的 SBOM 片段大概长这样:

{
  "bomFormat": "CycloneDX",
  "specVersion": "1.4",
  "components": [
    {
      "type": "library",
      "name": "lodash",
      "version": "4.17.21",
      "purl": "pkg:npm/lodash@4.17.21",
      "licenses": [{"license": {"id": "MIT"}}]
    },
    {
      "type": "library",
      "name": "axios",
      "version": "0.21.1",
      "purl": "pkg:npm/axios@0.21.1",
      "dependencies": [
        {"ref": "pkg:npm/follow-redirects@1.13.1"}
      ]
    }
  ]
}

嗯,这里要注意:purl(Package URL)是组件的“身份证号”,格式统一,方便工具自动解析。我个人强烈建议所有 SBOM 都带上 purl,否则后面做自动化漏洞匹配时会很痛苦。

1.2 SBOM 的格式标准:SPDX vs CycloneDX

目前主流的标准有两个:SPDX 和 CycloneDX。很多人问我该选哪个,我的回答是——看场景。

对比维度 SPDX CycloneDX
出身 Linux 基金会,2011 年起步 OWASP,2017 年起步
核心定位 许可证合规 + 版权管理 安全漏洞 + 供应链风险
漏洞支持 基础(通过外部引用) 原生支持(VEX、漏洞评分)
格式 Tag-value、JSON、YAML、RDF/XML JSON、XML、Protobuf
适用场景 法务审计、开源合规 DevSecOps、漏洞管理、SBOM 交换

我曾经在一个项目里同时用了两种格式:SPDX 交给法务部门做许可证审计,CycloneDX 交给安全团队做漏洞扫描。结果两边各取所需,配合得挺好。

避坑指南:我曾经见过一个团队,为了“统一标准”强行把所有 SBOM 转成 SPDX,结果 CycloneDX 原生的漏洞评分字段全丢了,导致后续的优先级排序完全跑偏。所以我的建议是:不要为了统一而统一,选对工具比选“最好”的工具更重要。

1.3 两种格式的实战对比

咱们直接看代码,感受一下区别。先看 SPDX 的 JSON 格式:

{
  "spdxVersion": "SPDX-2.3",
  "dataLicense": "CC0-1.0",
  "SPDXID": "SPDXRef-DOCUMENT",
  "name": "my-app-sbom",
  "packages": [
    {
      "SPDXID": "SPDXRef-Package-1",
      "name": "lodash",
      "versionInfo": "4.17.21",
      "licenseConcluded": "MIT",
      "checksums": [
        {"algorithm": "SHA256", "value": "a7f4e3b..."}
      ]
    }
  ]
}

再看 CycloneDX 的 JSON 格式:

{
  "bomFormat": "CycloneDX",
  "specVersion": "1.4",
  "metadata": {
    "timestamp": "2024-01-15T10:00:00Z",
    "tools": [{"vendor": "my-org", "name": "sbom-generator"}]
  },
  "components": [
    {
      "type": "library",
      "name": "lodash",
      "version": "4.17.21",
      "purl": "pkg:npm/lodash@4.17.21",
      "properties": [
        {"name": "owasp:risk_score", "value": "7.5"}
      ]
    }
  ]
}

看出来了吗?SPDX 更侧重“这个组件用了什么许可证”,而 CycloneDX 直接内置了安全属性(比如 owasp:risk_score)。如果你要做漏洞优先级排序,CycloneDX 天然更友好。

1.4 知识体系总览

为了让你对整个章节有个直观印象,我画了张图:

SBOM 基础概念知识体系 SBOM 定义与作用 组成要素 格式标准 软件成分表 供应链透明 组件标识 依赖关系 SPDX CycloneDX 核心要点 • SBOM 是软件供应链安全的基础设施 • 漏洞优先级排序依赖 SBOM 的完整性和准确性 • SPDX 偏合规,CycloneDX 偏安全,按需选择

1.5 为什么 SBOM 对漏洞优先级排序如此重要?

这个问题我经常被问到。其实答案很简单:没有 SBOM,你连自己用了什么都不知道,谈何排序?

想象一下这个场景:某天爆出一个高危漏洞 CVE-2024-XXXX,影响的是 log4j-core 2.14.0 到 2.16.0 版本。如果你的团队没有 SBOM,你需要手动翻遍所有项目的 pom.xml、package.json、requirements.txt……运气好半天能查完,运气不好漏掉一个就等着被通报吧。

但如果你有 SBOM,直接一条命令:

# 用 grype 扫描 SBOM 中的漏洞
grype sbom:my-app-sbom.json

# 或者用 trivy
trivy sbom my-app-sbom.json

几分钟内就能拿到完整的漏洞清单,而且还能按严重程度排序。这就是 SBOM 的价值——把“大海捞针”变成“按图索骥”。

我的建议:从今天开始,把 SBOM 生成集成到你的 CI/CD 流水线里。每次构建都自动生成一份 SBOM,存档到统一的仓库。三个月后你会感谢自己的这个决定。

1.6 小结

这一章我们聊了 SBOM 是什么、由什么组成、有哪些格式标准。说白了,SBOM 就是软件供应链的“配料表”,没有它,漏洞管理就是盲人摸象。