SBOM 的组成要素:拆开来看,其实就五样东西
说实话,我刚接触 SBOM 那会儿,觉得这东西挺玄乎的。什么 CycloneDX、SPDX 标准,一堆 XML 和 JSON,看着就头大。但后来我亲手搭过几次供应链安全体系之后,才明白——SBOM 说白了就是一张软件的「成分清单」,跟食品包装袋背后的配料表一个道理。
那这张清单里到底要写什么?我习惯把它拆成五个核心要素。你把这五个要素搞清楚了,SBOM 就算入门了。
1. 组件名称:你用的是哪个「东西」?
组件名称,就是软件里引用的那个库、框架或者工具的名字。比如 log4j、spring-boot、openssl。听起来简单吧?但坑就在这里。
commons-logging,在 Maven 中央仓库叫 commons-logging:commons-logging,但在 npm 里压根没有。如果你只写个「logging」,下游根本不知道你用的是哪个。
所以,我建议组件名称一定要带上命名空间(namespace)或者分组(group)信息。比如:
- Maven:
org.apache.logging.log4j:log4j-core - npm:
@angular/core - PyPI:
django==4.2
嗯,这样才不会有歧义。
2. 版本号:精确到补丁级别
版本号是 SBOM 里最容易出问题的地方。很多人写个 1.0 就完事了,但 1.0 和 1.0.1 可能差了一个严重漏洞的修复。
我个人习惯是:版本号必须精确到 patch 版本,也就是三位数:主版本.次版本.补丁版本。比如 4.17.2,而不是 4.17。
^4.17.0)在 SBOM 里是不允许的。SBOM 要的是「锁定版本」,不是「版本范围」。你想想看,如果只知道一个范围,漏洞扫描器怎么知道你到底有没有中招?
3. 依赖关系:谁依赖谁?
这是 SBOM 里最复杂的一块。一个组件可能依赖十个子组件,每个子组件又依赖更多。我见过最夸张的一个项目,光依赖树就打印了 2000 多行。
依赖关系通常有两种表达方式:
| 类型 | 说明 | 示例 |
|---|---|---|
| 直接依赖 | 你在代码里显式引用的组件 | spring-boot-starter-web |
| 传递依赖 | 被直接依赖所依赖的组件 | spring-boot-starter-web 依赖 tomcat-embed-core |
为什么要区分这个?因为漏洞修复的优先级不一样。直接依赖你还能控制,传递依赖有时候连升级都费劲。我在项目里遇到过,一个传递依赖的漏洞,因为上游库不更新,我们被迫自己打补丁——那叫一个折腾。
4. 许可证信息:合规的「红线」
很多人觉得许可证是法务的事,跟技术没关系。但说实话,许可证问题能直接让产品下架。我见过一个创业公司,因为用了 GPL 协议的库,结果整个产品被迫开源,投资人差点撤资。
SBOM 里需要记录的许可证信息包括:
- 许可证名称:比如 MIT、Apache 2.0、GPL 3.0
- 许可证文本或链接:方便法务核对
- 许可证兼容性:多个组件的许可证是否冲突
5. 哈希值:给组件上个「指纹」
哈希值,说白了就是给组件内容算一个唯一的「指纹」。常用的算法有 SHA-256、SHA-1(不推荐了)。
为什么需要哈希?因为名字和版本号都可以伪造。你下载了一个叫 log4j-2.14.1.jar 的文件,但谁能保证它真的是 Apache 官方发布的?哈希值就是用来做这个验证的。
我建议的做法是:
# 生成组件的 SHA-256 哈希
sha256sum log4j-core-2.17.2.jar
# 输出:a1b2c3d4...(64位十六进制字符串)
# 在 SBOM 中记录
"hashes": [
{
"alg": "SHA-256",
"value": "a1b2c3d4e5f6..."
}
]
一张图看懂 SBOM 五要素
下面这张图,是我自己画的一个 SBOM 核心结构。你看一眼,基本就记住这五个要素了。
五要素之间的关系
这五个要素不是孤立的。我举个例子你就明白了:
假设你的项目里引用了 log4j-core,版本是 2.17.2,它的哈希值是 sha256:a1b2c3...,许可证是 Apache 2.0。同时,它依赖了 log4j-api 的 2.17.2 版本。这五条信息合在一起,才构成一个完整的 SBOM 条目。
嗯,这一章的内容就到这儿。五要素记住了吗?组件名称、版本号、依赖关系、许可证信息、哈希值——下次你看到一份 SBOM,按这五个维度去检查,基本不会漏东西。