4. SBOM实战

好,咱们进入SBOM的实战环节。说实话,SBOM这个概念在圈子里喊了好几年,但真正动手做的人,其实没那么多。我见过不少团队,安全策略写得天花乱坠,结果一问SBOM怎么生成,全员沉默。嗯,今天咱们就把这事落地。

这一章,我会带你走完三个关键动作:用syft给容器镜像生成SBOM、用cyclonedx-maven-plugin给Java项目生成SBOM,以及SBOM的验证与签名。说白了,就是让你从“听说过”变成“能干出来”。

4.1 使用syft生成容器镜像SBOM

先聊容器镜像。你想想看,一个生产环境的镜像里,可能塞了几百个系统包、几十个应用依赖。哪个版本有漏洞?哪个库该升级?没有SBOM,你就是在黑盒里猜。

我个人习惯用syft来做这件事。它轻量、快,而且支持多种输出格式,包括CycloneDX和SPDX。安装很简单,一行命令搞定:

# macOS / Linux
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin

# 或者用 Docker 直接跑
docker run -v /var/run/docker.sock:/var/run/docker.sock anchore/syft:latest <镜像名>

生成SBOM的命令也特别直白。比如我要分析一个nginx镜像:

syft nginx:latest -o cyclonedx-json > nginx-sbom.json

这里我指定了-o cyclonedx-json,输出标准的CycloneDX格式。为什么选这个?因为CycloneDX是目前供应链安全领域最主流的格式,很多工具都认它。

小技巧: 如果你只想看结果,不想保存文件,直接跑 syft nginx:latest 不加参数,它会以表格形式打印到终端。我在做快速排查时经常这么用。

跑完之后,你会得到一个JSON文件。打开看看,里面包含了镜像里每一个组件的名称、版本、类型、许可证等信息。嗯,这就是你的“软件物料清单”。

我曾经在一个客户的CI流水线里集成syft,每次构建完镜像自动生成SBOM并归档。后来有一次漏洞爆发,他们半小时内就定位到了受影响的镜像版本。你说这值不值?

4.2 使用cyclonedx-maven-plugin生成Java项目SBOM

容器镜像搞定了,那Java项目呢?Java生态的依赖管理本来就复杂,Maven的pom.xml里层层传递依赖,一不小心就引入一堆你不知道的包。

这里我推荐cyclonedx-maven-plugin。它是CycloneDX官方维护的Maven插件,能直接解析你的项目依赖,生成标准的SBOM。

配置方式很简单,在pom.xml里加上:

<plugin>
    <groupId>org.cyclonedx</groupId>
    <artifactId>cyclonedx-maven-plugin</artifactId>
    <version>2.7.9</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>makeAggregateBom</goal>
            </goals>
        </execution>
    </executions>
</plugin>

然后执行:

mvn package

插件会在target/目录下生成一个bom.json文件。你也可以单独运行:

mvn org.cyclonedx:cyclonedx-maven-plugin:makeAggregateBom

这里有个细节要注意:makeAggregateBom会聚合所有模块的依赖,适合多模块项目。如果你的项目是单模块,用makeBom也行。

避坑指南: 我曾经遇到过一个项目,生成SBOM时漏掉了部分传递依赖。后来发现是Maven的scope设置问题——有些依赖被标记为provided或test,插件默认会排除它们。如果你需要包含所有依赖,记得加参数:-Dcyclonedx.includeTestScope=true。

生成的SBOM长什么样?大概是这样:

{
  "bomFormat": "CycloneDX",
  "specVersion": "1.4",
  "components": [
    {
      "type": "library",
      "name": "log4j-core",
      "version": "2.17.1",
      "purl": "pkg:maven/org.apache.logging.log4j/log4j-core@2.17.1"
    }
  ]
}

看到那个purl了吗?它是包的统一资源标识符,方便后续工具自动匹配漏洞库。说白了,这就是SBOM的“身份证号”。

4.3 SBOM的验证与签名

SBOM生成了,然后呢?直接扔给安全团队?别急,这里有个关键问题:你怎么保证这份SBOM是完整的、没被篡改过的?

你想想看,如果攻击者偷偷改了你的SBOM,把有漏洞的组件版本改成安全的版本,那你后续的漏洞扫描就全废了。所以,签名和验证是必须的。

我一般用cosign来做这件事。cosign是Sigstore项目的一部分,支持对容器镜像、文件、SBOM等进行签名。操作起来也不复杂。

首先,安装cosign:

# macOS
brew install cosign

# Linux
curl -O -L "https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64"
chmod +x cosign-linux-amd64
sudo mv cosign-linux-amd64 /usr/local/bin/cosign

然后,生成一个密钥对(或者用OIDC身份认证,我个人习惯用密钥对):

cosign generate-key-pair

这会生成cosign.key(私钥)和cosign.pub(公钥)。私钥一定要保管好,别往代码仓库里传。

接下来,对SBOM文件签名:

cosign sign-blob --key cosign.key nginx-sbom.json > nginx-sbom.json.sig

这会生成一个签名文件nginx-sbom.json.sig。验证的时候,用公钥:

cosign verify-blob --key cosign.pub --signature nginx-sbom.json.sig nginx-sbom.json

如果输出Verified OK,说明SBOM没被改过。嗯,就是这么简单。

核心要点: SBOM的签名不是为了防君子,而是为了防小人。在供应链安全里,信任是层层传递的。你签了名的SBOM,下游团队才能放心地用。

我还见过一种做法,是把签名信息直接嵌入SBOM文件本身。CycloneDX格式支持在signature字段里存放签名数据。这样你只需要一个文件,就能同时包含物料清单和签名信息。不过,我个人觉得分开存放更灵活,看你的场景。

小结

这一章的内容,说白了就是三件事:生成SBOM、生成SBOM、保护SBOM。容器镜像用syft,Java项目用cyclonedx-maven-plugin,最后用cosign签名验证。每一步都不复杂,但串联起来,就是一条完整的供应链安全防线。

我记得有一次在客户现场,他们被审计要求提供所有生产环境的SBOM。我帮他们搭了一套自动化流水线,每天凌晨自动扫描所有镜像和项目,生成SBOM并签名归档。审计那天,客户直接把归档目录甩过去,半小时就过了。嗯,这就是实战的价值。