
软件物料清单的概念并非凭空出现,它最早源于制造业和传统供应链管理。在汽车、航空等领域,制造商早就需要为每个零件提供详细的来源、规格和合规证明。当软件开发开始大规模采用组件化、开源化协作模式时,类似的透明性需求便自然浮现出来。一个典型的现代Web应用,其自行编写的代码可能只占整体代码量的10%-20%,其余80%以上均来自开源组件和第三方库。开发者通过npm、Maven、PyPI等包管理器引入这些依赖,却很少去深究这些被自动拉取的“零件”背后还有哪些传递性依赖。这种不透明性为安全风险埋下了伏笔。SBOM的作用就是打破这种黑盒状态,用标准化的方式回答“软件里到底有什么”这个看似简单、实则复杂的问题。
SBOM需要包含哪些核心信息
一份合格的SBOM并非简单的依赖列表,它需要具备足够的机器可读性和可操作性,以便集成到自动化的安全扫描和合规流程中。根据美国NTIA(国家电信和信息管理局)提出的最低元素框架,SBOM至少应包含三个层面的数据。首先是组件标识信息,也就是软件里用到的每一个组件叫什么、由谁提供、版本号是什么。这部分通常会采用包URL或软件标识符(SWID)等唯一命名方式,避免名称冲突带来的误判。其次是供应链关系信息,需要明确指出各个组件之间的依赖树或依赖图。仅仅罗列出所有组件是不够的,安全工具必须理解“组件A依赖了组件B的1.2.3版本”这样的关系,才能精准推断出漏洞的影响范围。最后是基线元数据,包括SBOM自身的版本、创建时间、创建工具以及整个描述对象的名称和版本等。这些元数据保证了SBOM的可追溯性,在跨组织共享时至关重要。
以实际场景为例,假设一个Java项目使用了Spring Boot框架,那么其SBOM会记录Spring Boot自身的GAV坐标、版本,同时将其直接依赖的spring-core、spring-context、jackson-databind等一众第三方库全部展开,直至叶子节点。如果jackson-databind的某个版本存在反序列化漏洞,安全团队可以直接在SBOM中搜索该组件,并立即定位到所有受影响的传递依赖路径。除了安全漏洞,许可证信息也是SBOM的核心组成部分。许多开源许可证如GPL具有较强的传染性,如果商业软件无意中引入了GPL授权的组件,可能会面临开源合规风险。在SBOM中显式记录每个组件的许可证标识符,能够让法务和合规团队提前介入把关,避免产品上线后才发现许可证冲突。
为了满足不同组织的需求,SBOM在实际实现上需要兼顾完整性和可定制性。过大的SBOM可能包含上万条组件记录,对存储和查询性能都是考验,因此一些工具支持按模块或按微服务粒度生成分片SBOM。同时,随着软件版本的快速迭代,SBOM也需要保持同步更新。理想的流程是在每次CI/CD构建完成后,自动生成一份新的SBOM并将其与构建产物一起归档,形成不可篡改的制品清单。这样在出现安全事件时,就可以追溯到具体哪个版本的制品中包含了问题组件,从而快速确定修复范围。
主流SBOM标准与格式对比
虽然SBOM的概念已经形成共识,但在落地层面存在多种数据格式标准,其中最具影响力的三种分别是SPDX、CycloneDX和SWID。SPDX(软件包数据交换)由Linux基金会主导,最初侧重于许可证合规,后来逐步扩展为覆盖安全、版权等多领域的完整标准。SPDX支持多种序列化格式,如tag-value、RDF、JSON、YAML,以及最新的SPDX 3.0甚至开始支持链接式数据模型。其规范严谨、字段覆盖面广,适合大型企业级项目的精细化管理。一段典型的SPDX JSON片段如下所示,它会为每个包分配唯一的SPDXID,并通过relationship字段描述包之间的依赖关系。
{
"spdxVersion": "SPDX-2.3",
"dataLicense": "CC0-1.0",
"SPDXID": "SPDXRef-DOCUMENT",
"name": "my-app-sbom",
"documentNamespace": "https://ippipp.com/sbom/my-app/1.0",
"creationInfo": {
"created": "2023-04-15T08:00:00Z",
"creators": ["Tool: MySBOMGenerator-1.0"]
},
"packages": [
{
"SPDXID": "SPDXRef-my-app",
"name": "my-app",
"versionInfo": "1.0.0",
"supplier": "Organization: ExampleOrg"
},
{
"SPDXID": "SPDXRef-spring-boot",
"name": "spring-boot-starter-web",
"versionInfo": "2.7.5",
"downloadLocation": "https://repo1.maven.org/...",
"licenseConcluded": "Apache-2.0"
}
],
"relationships": [
{
"spdxElementId": "SPDXRef-my-app",
"relatedSpdxElementId": "SPDXRef-spring-boot",
"relationshipType": "CONTAINS"
}
]
}
CycloneDX则是由OWASP基金会推动的轻量级SBOM标准,原生包含对于OWASP Top 10漏洞信息的描述能力。它的设计哲学是简洁、易于集成,特别适合DevOps场景。CycloneDX主要使用JSON和XML格式,其结构围绕bom、metadata、components、services展开。一个关键特性是它支持通过组件中的evidence字段记录取证信息,比如通过文件哈希来证明某个组件确实存在于制品中,增强了SBOM的可信度。此外,CycloneDX还可以描述服务依赖,适合微服务架构下的SBOM建模。
SWID(软件标识标签)则是ISO/IEC 19770-2国际标准,源自IT资产管理领域。SWID标签通常以XML形式嵌入在软件包内部,主要用于帮助IT部门识别和管理已安装的软件。与SPDX和CycloneDX相比,SWID更侧重于运营阶段的软件发现和合规,它的生成往往由软件供应商在构建时就植入,而非依赖第三方扫描工具。因此,这三种标准并非互斥,而是可以互相转换和补充。NTIA的指南中也明确指出,不同类型的组织应根据自身需求选择或组合使用这些标准。一些商业SBOM平台甚至能够将SPDX、CycloneDX和SWID三种格式进行双向转换,以打通开发、安全、IT运维等多个环节。
如何在开发流水线中自动生成SBOM
手工维护SBOM是不切实际的,只有将SBOM生成无缝嵌入CI/CD流水线,才能保证其时效性和准确性。目前已有大量开源和商业工具支持在构建阶段自动生成SBOM。对于Java项目,可以使用OWASP Dependency-Check或Maven的spdx-maven-plugin插件,在package阶段输出SBOM文件。对于Node.js项目,npm自带的清单已经可以视为一种原始SBOM,但更推荐使用cyclonedx-npm插件将package-lock.json直接转换为标准CycloneDX格式。在容器镜像领域,Syft和Grype是非常流行的两个工具,Syft负责生成容器镜像的SBOM,而Grype则基于SBOM进行漏洞扫描。一条典型的流水线步骤可能是:先通过Docker构建镜像,然后运行 syft myimage:latest -o cyclonedx-json > myimage.sbom.json,将生成的SBOM文件归档到制品仓库或漏洞管理平台,随后Grype可以根据这份SBOM生成漏洞报告。
除了语言和容器维度的工具,还有一些平台级的SBOM生成方案,例如Sigstore的Cosign配合Rekor可以实现对SBOM的签名与透明性日志记录,确保SBOM本身没有被篡改。在Kubernetes生态中,Kubernetes自身的SBOM也已经可以通过构建时的元数据自动提取。对于多语言混合的复杂项目,可以引入诸如 ORT(OSS Review Toolkit)这种综合性工具,它能够扫描整个代码仓库和依赖声明文件,生成一份涵盖所有子模块的统一SBOM。在流水线中落地SBOM的关键是要将其作为制品的一部分,与二进制、容器镜像等一同存放并建立索引。例如,可以在Harbor镜像仓库中配置Webhook,在镜像推送后自动触发SBOM解析任务,将组件信息注入漏洞扫描器,形成“构建——生成SBOM——扫描——入库”的自动化闭环。
值得注意的是,SBOM的生成时机和粒度也需要谨慎设计。如果每次提交都重新扫描整个代码树,耗时较长且可能产生过多冗余SBOM。比较好的实践是结合制品版本号来生成SBOM,每打出一个候选版本或发布版本时,才触发完整的依赖解析和SBOM导出。对于包含数十个微服务的系统,可以为每个服务单独生成SBOM,同时生成一个顶层聚合SBOM,描述各个服务之间的调用关系和整体构成。这种分层结构既照顾到了单个服务的详细度,又提供了系统全景视图,在排查跨服务传递依赖时尤其有用。
SBOM面临的实际挑战与实施建议
尽管SBOM的价值已被广泛认可,但在大规模落地过程中仍存在不少痛点。首先是组件的标识一致性问题,同一个开源包在不同包管理器中的名称可能完全不同,例如Java中的log4j在Maven中为log4j:log4j,而在Gradle中可能写作org.apache.logging.log4j:log4j-core。如果没有一套统一的命名规范或映射表,SBOM中的数据就会产生大量噪音,导致工具无法准确关联CVE漏洞。这需要行业共同推动软件标识的标准化,比如采用GitHub的Advisory Database中使用的公共标识符,或利用Package URL方案为每个组件生成唯一ID。
其次,SBOM的数据完整度高度依赖于扫描工具的解析能力。如果项目使用了非标准的依赖引入方式,例如通过脚本动态下载JAR包、使用子模块引用外部代码等,工具很难完全抓取这些隐含依赖。因此,组织需要制定严格的依赖引入规范,尽可能将所有依赖声明在标准的配置文件中,并禁止私自从URL拉取未经审计的二进制包。对于遗留系统,可以先通过运行时分析工具(如Java的ClassAgent)采集实际加载的类,反推缺失的依赖,补充到SBOM中。
最后,SBOM的共享和交换机制也需要有章可循。当软件供应商向客户交付产品时,是否必须提供SBOM?SBOM应以何种方式随同交付?目前业界普遍采用的做法是在发布页面附带SBOM文件下载链接,或者将SBOM嵌入到容器镜像标签的Annotation中。对于SaaS类服务,用户无法直接获取二进制,但仍然可以通过API查询其服务所使用的组件清单。推行SBOM不仅是技术上的变革,更涉及采购合约、安全责任划分等商业合作模式的调整。建议企业从内部项目开始试点,逐步积累SBOM数据的消费经验,再向上下游合作伙伴推广。有了海量SBOM数据的支撑,整个软件生态的安全透明度才能实现质的飞跃。