SBOM 标准与格式:SPDX、CycloneDX、SWID 深度解析

聊 SBOM,绕不开格式问题。就像写病历要用统一模板,不同医院才能看懂。SBOM 也一样——格式不统一,上下游根本没法协作。

目前主流的三种格式:SPDX、CycloneDX、SWID。我这些年做供应链安全,三种都踩过坑。今天把它们的核心逻辑、适用场景、以及我个人的选型经验,一次性说清楚。

SBOM 三大标准格式对比 SPDX 许可证 & 版权 CycloneDX 安全 & 依赖 SWID 软件身份 & 安装 核心特性 • 许可证合规分析 • 文件级溯源 • 社区成熟度高 核心特性 • 漏洞关联 (CVE) • 依赖关系图 • 轻量 JSON 格式 核心特性 • 软件身份标识 • 安装/补丁管理 • ISO 19770-2 标准 选型建议 许可证合规 → SPDX | 安全漏洞 → CycloneDX | 企业资产管理 → SWID 通用场景:SPDX + CycloneDX 组合使用

SPDX 标准详解

SPDX(Software Package Data Exchange)是 Linux 基金会维护的标准。它最早关注的是许可证合规——说白了,就是帮你搞清楚项目里用了哪些开源组件,它们的许可证能不能兼容。

我记得刚入行那会儿,公司有个产品因为用了 GPL 的库,差点被法务叫停。那时候要是早用 SPDX,根本不会出这种问题。

SPDX 的核心结构

一个 SPDX 文档包含几个关键部分:

  • 文档元信息:创建者、创建时间、文档命名空间
  • 包信息:包名、版本、下载位置、校验和
  • 文件信息:每个文件的许可证、版权、贡献者
  • 许可证信息:引用的许可证列表,包括自定义许可证
  • 关系:包与包、文件与包之间的依赖关系

SPDX 2.3 版本新增了安全参考字段,可以直接关联 CVE 编号。这意味着 SPDX 不再只是许可证工具,也开始涉足安全领域了。

来看一个简化的 SPDX JSON 示例:

{
  "spdxVersion": "SPDX-2.3",
  "dataLicense": "CC0-1.0",
  "SPDXID": "SPDXRef-DOCUMENT",
  "name": "my-app-1.0.0",
  "packages": [
    {
      "SPDXID": "SPDXRef-Package-log4j",
      "name": "log4j",
      "versionInfo": "2.17.1",
      "licenseDeclared": "Apache-2.0",
      "checksums": [
        {
          "algorithm": "SHA256",
          "checksumValue": "a1b2c3d4e5..."
        }
      ]
    }
  ],
  "relationships": [
    {
      "spdxElementId": "SPDXRef-DOCUMENT",
      "relatedSpdxElement": "SPDXRef-Package-log4j",
      "relationshipType": "DESCRIBES"
    }
  ]
}

我的习惯:生成 SPDX 时,一定把 licenseDeclared 和 licenseConcluded 都填上。前者是组件声明的许可证,后者是你审计后确认的。两者不一致的情况,我见过太多次了。

CycloneDX 标准详解

CycloneDX 是 OWASP 社区推出的标准。它的定位很明确——为安全而生。如果你主要关心漏洞管理、依赖风险,CycloneDX 会更顺手。

我曾经帮一个金融客户做软件供应链审计,他们要求每个组件必须关联 CVE。SPDX 当时还不支持这个,CycloneDX 直接原生支持,省了我不少事。

CycloneDX 的核心特性

  • 组件依赖图:支持 dependsOn 关系,能画出完整的依赖树
  • 漏洞引用:直接嵌入 vulnerabilities 数组,关联 CVE、CVSS 评分
  • 服务组件:不光能描述软件包,还能描述 API、微服务
  • 轻量级:默认 JSON 格式,解析快,适合 CI/CD 流水线

CycloneDX 的依赖关系描述非常直观:

{
  "bomFormat": "CycloneDX",
  "specVersion": "1.4",
  "components": [
    {
      "type": "library",
      "name": "jackson-databind",
      "version": "2.13.2",
      "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.2"
    }
  ],
  "dependencies": [
    {
      "ref": "pkg:maven/my-app/my-app@1.0.0",
      "dependsOn": [
        "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.2"
      ]
    }
  ],
  "vulnerabilities": [
    {
      "id": "CVE-2022-25647",
      "source": {
        "name": "NVD",
        "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-25647"
      },
      "ratings": [
        {
          "score": 7.5,
          "severity": "high"
        }
      ]
    }
  ]
}

我曾经踩过的坑:CycloneDX 的 purl(Package URL)格式必须严格规范。少写一个斜杠,或者类型写错,解析器直接报错。建议用工具自动生成,别手写。

SWID 标签介绍

SWID(Software Identification)标签是 ISO 19770-2 标准。它跟 SPDX、CycloneDX 不太一样——SWID 更关注软件的身份和安装状态。

你想想看,IT 资产管理团队要统计公司装了多少软件、哪些需要升级。SWID 就是干这个的。它由软件发布商生成,嵌入安装包中。

SWID 的核心结构

  • 实体信息:软件发布商、版本、唯一 ID
  • 元数据:安装路径、大小、校验和
  • 链接关系:补丁、更新包、依赖组件
  • 签名:数字签名保证标签未被篡改

SWID 标签通常以 XML 格式存在:

<SoftwareIdentity
  xmlns="urn:iso:std:iso:19770:-2:2015"
  name="MyApp"
  version="1.0.0"
  tagId="com.example.myapp-1.0.0">
  <Entity name="Example Corp" role="softwareCreator"/>
  <Meta persistentId="com.example.myapp"/>
  <Link rel="patch" href="https://updates.example.com/patch-1.0.1.swid"/>
</SoftwareIdentity>

实际场景:SWID 在 Windows 生态里用得比较多。微软的 MSI 安装包默认就带 SWID 标签。Linux 下反而少见,更多用 SPDX 或 CycloneDX。

三种格式的对比与选型

三种格式各有侧重,没有绝对的「最好」。我一般根据场景来选:

维度 SPDX CycloneDX SWID
维护组织 Linux Foundation OWASP ISO/IEC
核心关注点 许可证合规 安全漏洞 软件身份
格式支持 JSON / XML / RDF / Tag JSON / XML XML
依赖关系 支持(关系类型丰富) 支持(依赖树清晰) 有限(主要补丁关系)
漏洞关联 2.3 版本开始支持 原生支持 不支持
许可证描述 非常详细 基础支持 不支持
工具生态 成熟(FOSSology、ORT) 活跃(OWASP 工具链) 企业级(微软、IBM)

选型建议

我个人习惯这样选:

  • 做开源合规审计 → 选 SPDX。它的许可证描述能力最强,法务团队也认。
  • 做安全漏洞管理 → 选 CycloneDX。原生漏洞关联,CI/CD 集成最方便。
  • 做企业 IT 资产盘点 → 选 SWID。Windows 环境天然支持,省去生成成本。
  • 通用场景 → SPDX + CycloneDX 组合。我现在的项目就是两套都生成,各取所长。

一句话总结:SPDX 管「能不能用」,CycloneDX 管「安不安全」,SWID 管「装没装」。搞清楚这个,选型就不会错。