3、SBOM标准与格式:SPDX格式详解、CycloneDX格式详解、SWID标签介绍、三种格式的对比与选型

聊到SBOM,绕不开的就是格式问题。你想想看,如果大家各自用不同的方言描述同一个软件成分,那交换起来得多费劲?所以标准化就成了关键。目前业界主流的三种格式,我这些年都深度用过,各有各的脾气。今天咱们就掰开揉碎了聊聊。

3.1 SPDX格式详解

SPDX(Software Package Data Exchange)是Linux基金会牵头搞的,历史最久,生态也最成熟。我个人习惯把它看作是SBOM界的“普通话”——规范、严谨、覆盖面广。

SPDX的核心思路是用一个统一的文档来描述软件包、许可证、版权信息和文件级别的引用关系。它的文档结构非常清晰,主要包含以下几个部分:

  • 文档创建信息:谁创建的、什么时候、用什么工具。
  • 包信息:包名、版本、下载位置、校验和(SHA1/SHA256)。
  • 文件信息:每个文件的校验和、文件类型、版权信息。
  • 许可证信息:声明的许可证、实际检测到的许可证、许可证注释。
  • 关系信息:包与包之间的依赖关系(DEPENDS_ON、CONTAINS等)。

我在项目中遇到过最头疼的事,就是供应商给的固件里混了一堆GPL代码,但他们的SPDX文档里只字未提。后来我们强制要求所有上游组件必须附带SPDX 2.2格式的SBOM,才把这个问题堵住。

SPDX 2.3 版本的一个关键改进:引入了“安全参考”字段,可以直接关联CVE编号。这意味着你可以从SBOM直接跳转到漏洞库,省去了手动查表的功夫。

下面是一个简化的SPDX示例,展示了一个固件包的基本描述:

SPDXVersion: SPDX-2.3
DataLicense: CC0-1.0
SPDXID: SPDXRef-DOCUMENT
DocumentName: firmware-v3.2.1
Creator: Tool: sbom-tool-1.0
Created: 2024-03-15T10:00:00Z

##### Package: firmware-core
PackageName: firmware-core
SPDXID: SPDXRef-Package-firmware-core
PackageVersion: 3.2.1
PackageDownloadLocation: git+https://git.example.com/firmware-core.git
PackageChecksum: SHA256: abc123...
PackageLicenseConcluded: GPL-2.0-only AND MIT

##### Relationship
Relationship: SPDXRef-Package-firmware-core DEPENDS_ON SPDXRef-Package-libcrypto

我的小技巧:SPDX的校验和字段一定要填。我曾经见过一个SBOM,所有校验和都是空的,那这个SBOM基本等于废纸——你没法验证你拿到的二进制是不是就是文档里描述的那个。

3.2 CycloneDX格式详解

CycloneDX是OWASP社区推的格式,一开始就冲着安全场景去的。说白了,它比SPDX更“轻量”,更专注于漏洞管理和依赖分析。如果你主要关心“这个组件有没有漏洞”,那CycloneDX用起来会更顺手。

CycloneDX的JSON结构非常紧凑,核心字段包括:

  • bomFormat:固定为"CycloneDX"。
  • specVersion:格式版本号,目前主流是1.5。
  • components:组件列表,每个组件包含name、version、purl、licenses等。
  • dependencies:依赖关系图,用ref字段关联。
  • vulnerabilities:直接嵌入漏洞信息,包括CVE、CVSS评分、修复建议。

我记得有一次做固件安全审计,客户要求我们提供所有第三方库的漏洞清单。如果用SPDX,我得额外写脚本去查CVE库。但CycloneDX直接支持在SBOM里内嵌漏洞数据,一条命令就搞定了。嗯,这就是它设计上的优势。

注意:CycloneDX的purl(Package URL)字段非常关键。它像是一个组件的“身份证”,格式为 pkg:type/namespace/name@version。如果purl写错了,自动化的漏洞匹配工具就会认不出来。我见过有人把purl的斜杠方向写反,结果整个依赖分析全乱了。

一个典型的CycloneDX片段长这样:

{
  "bomFormat": "CycloneDX",
  "specVersion": "1.5",
  "components": [
    {
      "type": "library",
      "name": "libcurl",
      "version": "7.88.1",
      "purl": "pkg:generic/libcurl@7.88.1",
      "licenses": [
        {
          "license": {
            "id": "MIT"
          }
        }
      ]
    }
  ],
  "dependencies": [
    {
      "ref": "pkg:generic/firmware-core@3.2.1",
      "dependsOn": [
        "pkg:generic/libcurl@7.88.1"
      ]
    }
  ]
}

3.3 SWID标签介绍

SWID(Software Identification)标签是ISO/IEC 19770-2标准,跟前面两个不太一样。它更侧重于软件安装后的识别和管理,说白了就是“装到机器上了,我怎么知道它是谁”。

SWID标签通常以XML格式存在,嵌入在安装包或固件镜像里。它的核心元素包括:

  • SoftwareIdentity:根元素,包含name、version、tagId。
  • Entity:软件发布者信息。
  • Evidence:安装证据,比如文件路径、注册表项。
  • Payload:软件包含的文件清单。

我在做固件供应链管理时,发现SWID标签在Windows生态里用得特别多。微软的很多组件都自带SWID标签,方便企业做资产盘点。但在嵌入式Linux固件里,SWID的普及率远不如SPDX和CycloneDX。

SWID的一个独特优势:它支持“证据”字段,可以记录软件安装后留下的痕迹。这对于固件运行时验证非常有用——你可以通过检查SWID标签里的文件哈希,确认固件没有被篡改。

3.4 三种格式的对比与选型

好了,三种格式都介绍完了。你可能会问:“那我到底该用哪个?” 别急,我整理了一张对比表,你看完心里就有数了。

维度 SPDX CycloneDX SWID
标准化组织 Linux基金会 OWASP ISO/IEC
核心用途 许可证合规 + 成分披露 漏洞管理 + 依赖分析 软件安装识别 + 资产盘点
数据格式 Tag/Value、JSON、RDF/XML JSON、XML XML
漏洞支持 2.3版本开始支持 原生支持,可内嵌CVE 弱,需外部关联
依赖关系 支持,关系类型丰富 支持,图结构清晰 有限,主要描述包含关系
工具生态 非常成熟,FOSSology等 快速增长,OWASP工具链 较封闭,厂商绑定多
固件适用性 高,适合复杂许可证场景 高,适合安全审计场景 中,适合Windows/商业软件

选型上,我个人的建议是这样的:

  • 如果你的主要痛点是许可证合规(比如怕GPL污染),选SPDX。它的许可证字段最完善,法律团队也认。
  • 如果你的主要痛点是漏洞管理(比如每天要查CVE),选CycloneDX。它原生支持漏洞内嵌,自动化程度高。
  • 如果你在做企业资产盘点,或者你的固件跑在Windows IoT上,可以考虑SWID。但说实话,在嵌入式固件领域,SWID的生态还不够强。

我的实战经验:别死磕一种格式。我在一个项目里同时用了SPDX和CycloneDX——SPDX给法务看,CycloneDX给安全团队用。两边都满意,何乐而不为?

最后,我用一张图来总结这三种格式在固件生命周期中的定位:

SBOM三种格式在固件生命周期中的定位 SBOM三种格式在固件生命周期中的定位 开发阶段 构建阶段 部署阶段 运维 SPDX:许可证 + 成分 CycloneDX:漏洞 + 依赖 SWID:安装识别 说明: SPDX 覆盖开发到构建阶段,侧重许可证合规;CycloneDX 覆盖构建到运维阶段,侧重漏洞跟踪; SWID 主要覆盖部署和运维阶段,侧重安装后的资产识别。实际项目中可以组合使用。 推荐:固件场景首选 SPDX + CycloneDX 组合

这张图很直观地展示了:SPDX和CycloneDX在固件开发到运维的整个链条上都有覆盖,而SWID更偏向部署后的场景。我个人在固件项目里,基本是SPDX打底、CycloneDX做安全增强,SWID只在客户有明确要求时才加。