SBOM生成了也可能没用:先验证组件、版本和签名

|作者: QUASA 编辑团队|2 分钟阅读
SBOM生成了也可能没用:先验证组件、版本和签名

为 Python 项目生成可用于交付的 CycloneDX SBOM,应从实际发布环境中的已安装依赖导出清单,再验证格式、组件信息、版本和最终文件的签名。CycloneDX Python 用法文档支持扫描虚拟环境,也列出 requirements、Poetry 和 Pipenv 输入;下例选择 CycloneDX 1.6 JSON,并以发布环境为准。

这些检查需要成为 CI 的阻断步骤。CycloneDX CLI 命令说明给出格式验证、文件签名和验签方法;其中验证命令要带 --fail-on-errors,校验错误才会返回非零状态。组件缺失和意外多版本则需要项目另设规则,不能从格式校验通过直接推断清单完整。

从发布环境生成清单

Python 项目的虚拟环境生成对应的 CycloneDX SBOM,生成工具与应用依赖分开。

假设 Python 项目有 pyproject.toml,发布依赖已安装在 .venv 中,且 CI 工具环境可以运行 cyclonedx-py。生成命令为 cyclonedx-py environment .venv --pyproject pyproject.toml --spec-version 1.6 --output-format JSON --output-file sbom.json。--pyproject 用于提供根项目的元数据;environment 扫描指定环境中实际安装的包,因此应指向构建发布物时使用的环境。

不要先在应用的 .venv 中安装生成器,再把整个环境当作发布依赖扫描;那会把生成器及其依赖一并写入清单。可以让 CI 在独立的工具环境中运行 cyclonedx-py。如果构建会选择额外依赖组、安装本项目或重新解析依赖,应先完成这些步骤,再扫描最终环境。否则,SBOM 描述的可能是构建过程中的一个中间状态。

若交付依据是 requirements.txt,也可运行 cyclonedx-py requirements requirements.txt --pyproject pyproject.toml --spec-version 1.6 --output-format JSON --output-file sbom.json。使用 Poetry 或 Pipenv 时,应按相应的项目及锁文件生成,并核对所选依赖组是否与发布物一致。对于只写版本范围、未固定解析结果的依赖文件,扫描安装完成的环境更适合记录这次构建实际得到的版本。重新安装依赖或重新打包后,旧清单不能自动代表新产物。

验证格式与组件字段

对生成的文件运行 cyclonedx validate --input-file sbom.json --input-format json --input-version v1_6 --fail-on-errors。这里显式指定输入版本,使校验参数与生成时选择的规范版本一致。命令失败就停止流水线;命令成功表示这项格式检查已通过,仍需确认清单描述的是正确的项目和依赖。

IBM Cloud 的 SBOM 校验说明列出有内容的根组件、时间戳,以及每个组件的 name 和 bom-ref 等检查项。对需要按依赖版本追踪漏洞的 Python 发布物,还应把组件 version 非空设为项目的交付规则。这是针对本流程增加的内容要求,不应误写成 CycloneDX 规范对所有组件的统一要求。

CI 可以用 jq 对这些字段加一道门槛:jq -e '(.metadata.component.name // "" | length > 0) and (.metadata.timestamp // "" | length > 0) and (.components | type == "array") and all(.components[]; ((.name // "") | length > 0) and ((.version // "") | length > 0) and ((."bom-ref" // "") | length > 0))' sbom.json。命令返回非零状态时停止交付。若项目确实包含无法给出版本的组件,应为那类组件写明例外,而不是悄悄取消所有组件的版本检查。

字段齐全也不等于覆盖齐全。把清单中的直接依赖与本次构建使用的锁定结果核对;发现锁文件中的发布依赖不在 SBOM 中时,先确认扫描目标、依赖组和安装步骤。只检查 SBOM 内部的字段,无法发现从一开始就没有被扫描到的包。

识别同一环境中的意外多版本

版本检查要先确定比较范围:同一发布环境中的组件可以按 group 与规范化后的 name 分组,再统计每组不同的 version。Python 包名比较时应统一大小写,并把连字符、下划线和句点的连续变化归一;否则,同一个包可能被拆成几个组。完全相同的名称与版本重复出现,属于另一类重复记录问题,不应误报为多版本。

若本次 SBOM 只描述一个 Python 发布环境,可用以下 jq 命令把同名多版本设为失败:jq -e '[(.components // [])[] | {k: ((.group // "") + "/" + (.name | ascii_downcase | gsub("[-_.]+"; "-"))), v: .version}] | group_by(.k) | all(.[]; ([.[].v] | unique | length) == 1)' sbom.json。这条规则应在字段非空检查之后执行;jq 的布尔结果为 false 时,CI 收到非零退出状态。

例如,假设单一环境的清单同时列出 example-lib 1.2 和 2.0,而发布依赖只解析出其中一个版本,就应阻断交付并核对扫描输入。这是说明规则的条件示例,并非工具测试结果。如果清单合并了多个服务或运行环境,两个版本可能分别属于不同交付单元;应先分开检查,再决定是否允许合并。多版本本身提示需要核对,不能脱离交付范围直接判定为漏洞。

签署最终文件并核验签名

内容检查完成后,签署准备随发布物交付的那份 sbom.json。使用分离式文件签名时,签名步骤运行 cyclonedx sign file sbom.json --key-file private.key --signature-file sbom.json.sig;接收或部署步骤运行 cyclonedx verify file sbom.json --key-file public.key --signature-file sbom.json.sig。这里签署的是文件本身,验签时必须使用收到的同一份文件和对应的签名。

私钥应由受控签名步骤提供,不能随仓库或发布附件交付。验签方也必须预先信任公钥;如果从未受信任的附件中同时接受 SBOM、签名和公钥,验签就无法建立预期的发布者身份。签名之后只要修改 sbom.json,就需要重新签署。验签能发现文件与受信任密钥不匹配或签署后内容被改动,却不能证明文件中的依赖确实覆盖了发布物。

让 CI 在正确的阶段失败

CycloneDX SBOM 与分离式签名对应,文件改动后无法通过同一签名的核验。

流水线按安装发布依赖、生成清单、格式校验、字段与版本检查、签名、交付的顺序运行。签名之后的接收端或部署阶段再验签,并让各阶段的非零退出状态阻断后续步骤。这样,签名保护的是已完成内容检查的文件,验签核对的也是实际收到的文件。

  • 生成命令失败、没有产生 sbom.json,或格式校验返回非零状态时,停止构建。
  • 根组件或时间戳缺失、依赖组件缺少必需字段,或清单与本次发布的锁定依赖不符时,停止交付。
  • 单一发布环境中出现无法由依赖结果解释的同包多版本时,停止交付并核对扫描范围。
  • 签名文件缺失、验签所用公钥不受信任,或部署端验签失败时,拒绝使用该清单。

保存发布附件时,将 sbom.json、sbom.json.sig 与对应的构建标识关联。若依赖重新解析、发布物重新构建,或者清单在检查后被改写,应重新生成并执行检查与签名;沿用上一轮文件会让组件版本与当前发布物失去对应关系。

相关阅读:

分享:

订阅我们的新闻通讯

将最新 Web3、AI 和加密货币新闻直接发送到您的邮箱。

0