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 知识体系总览
为了让你对整个章节有个直观印象,我画了张图:
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 就是软件供应链的“配料表”,没有它,漏洞管理就是盲人摸象。