如何生成 SBOM?

解读

在国内 Rust 岗位面试中,SBOM(Software Bill of Materials,软件物料清单) 已不再是“加分项”,而是合规硬门槛:网信办《关键信息基础设施安全保护条例》、工信部《软件供应链安全三年行动计划》均要求上线系统必须提供可追溯的第三方组件清单。Rust 项目依赖链深、静态链接多,cargo 默认只生成 Cargo.lock,无法直接满足国内监管对“名称-版本-许可证-哈希-漏洞”五元组的格式要求。面试官问“如何生成 SBOM”,核心想考察两点:

  1. 你是否理解国内监管侧重点(许可证合规、漏洞闭环、国产密码算法替换);
  2. 能否在不破坏 Cargo 工作流的前提下,自动化输出符合 SPDX/ CycloneDX 标准、且能被信通院“铸基”平台、奇安信、悬镜等国产 SCM 工具识别的文件。

知识点

  1. Cargo 元数据接口cargo metadata --format-version 1 输出完整依赖图,包含 source、checksum、features。
  2. 国内常用 SBOM 规范
    • CycloneDX 1.5(xml/json):信通院白名单工具链原生支持,可携带“国产密码算法替换标记”扩展字段。
    • SPDX 2.3(tag-value/json):工信部抽检模板指定格式,需额外填充“下载位置”与“版权所有”字段。
  3. Rust 生态工具
    • cargo-cyclonedx:官方插件,一键生成 CycloneDX,支持 workspace;需加 --all-features 防止遗漏条件编译依赖
    • cargo-sbom(github.com/rust-secure-code/cargo-sbom):输出 SPDX,但默认不填充国内要求的“漏洞编号”字段,需二次加工
    • syft 0.90+:支持 cargo-auditable 注入的 ELF 指纹,可在二进制阶段补全“静态链接时实际进入的依赖”,解决“源码 SBOM 与交付物不一致”的审计痛点。
  4. 许可证合规:Rust 依赖常带 MIT OR Apache-2.0 双许可,需用 askalono-cli 扫描并人工确认最终选用条款,否则 SPDX 字段 LicenseConcluded 会被监管打回。
  5. 漏洞闭环cargo-audit 输出 JSON,通过 jq 把 advisory-id 注入 CycloneDX 的 vulnerabilities 节点,满足《信息安全技术 软件供应链安全要求》GB/T 43698-2024 的“漏洞可追溯”条款。
  6. CI 集成:GitLab 国内私有化实例、Gitee Go、以及各大银行自研 Jenkins 均支持上传 SBOM,需在 .gitlab-ci.yml 里把 SBOM 文件作为 artifacts:reports:cyclonedx 上传,供 SCM 门禁比对基线

答案

在工程实践中,我采用“源码 + 二进制双通道”方案,保证交付给监管和客户的 SBOM 与最终运行镜像 100% 一致,步骤如下:

  1. 源码阶段
    cargo install cargo-cyclonedx --version 0.4.0
    cargo cyclonedx --all-features --output-cdx-json
    
    生成 bom.json此时已包含所有 crates 的 name、version、checksum、license 表达式
  2. 许可证精修
    askalono crawl --format json . > license-map.json
    
    用 Python 脚本把 license-map.json 的结论回填到 bom.jsonlicenses 节点,确保 LicenseConcluded 字段与国产白名单匹配;若发现 GPL-3.0 等高风险许可,立即触发 MR 阻塞。
  3. 漏洞注入
    cargo audit --json > audit.json
    jq -s '.[0] * {vulnerabilities: .[1].vulnerabilities}' bom.json audit.json > bom-vuln.json
    
    这样 SBOM 里每个组件都携带 vulnerabilities 数组,满足监管“一组件一漏洞编号”要求
  4. 二进制阶段
    Cargo.toml 中加入
    [dependencies]
    auditable = { version = "0.7", features = ["serde"] }
    
    重新编译后,使用 syft 扫描 ELF
    syft packages mybin --output cyclonedx-json@1.5 > bin-sbom.json
    
    对比源码 SBOM,若出现 compiler_builtinslibc 静态链接版本差异,以二进制结果为准,并在 MR 说明中记录差异原因。
  5. CI 上传
    将最终 bom-vuln.jsonbin-sbom.json 压缩为 sbom.zip,通过银行内部 SCM 的 /api/v1/sbom/upload 接口上传,返回的 traceId 写入发布记录,完成合规闭环

拓展思考

  1. 多语言混合仓库:若 Rust 作为动态库被 Go/Java 通过 CGO/JNI 调用,需用 syft 对整个容器镜像做聚合扫描,再与 Rust 侧 SBOM 做 component.bom-ref 对齐,避免“重复计数”被监管扣分。
  2. 闭源依赖处理:国内厂商常提供 .a 静态库但不给源码,可在 CycloneDX 的 externalReferences 里添加 distribution-intake 类型 URL,指向厂商在“信通院软件供应链安全服务平台”备案的编号,实现“黑盒组件”合规。
  3. 国密算法替换场景:当使用 ring 0.16 时,默认带 AES-NI 指令集,若客户要求替换为国密 SM4,需自定义 feature 并重新编译,此时 SBOM 中 properties 节点需增加 {"name": "crypto-algorithm", "value": "SM4-GM/T 0002-2012"}供后续密码合规抽检快速过滤