如何用Maven和Gradle在Java中创建SBOM
#安全 #java #applicationsecurity

在Java构建应用程序时,我们高度依赖外部库和框架。并且每个导入的Java软件包也可能取决于更多库。这意味着您的应用程序中包含的Java软件包的数量通常并不是真正透明的。作为开发人员,这些嵌套(及物)依赖性创造了您可能不知道实际使用的所有库的问题。

最近,我们讨论了为什么以及如何仔细维护依赖性。在文章Best practices for managing Java dependencies中,我讨论了可用于设置依赖关系管理策略的选项和工具。但是,如果您将Java应用程序交付给客户怎么办?他们怎么知道包括哪些依赖关系?更重要的是,他们如何检查依赖项是否不容易受到安全问题的影响?答案是软件材料清单

什么是SBOM?

software bill of materials,通常被缩写为SBOM,是应用程序中使用的所有软件组件的列表。 SBOM由该组织建造的第三方开源库,供应商提供的软件包和第一方工件组成。您基本上可以将其视为应用程序的完整成分列表。

但是请注意不要将SBOM与Maven的材料清单(BOM)混淆。在Maven中,BOM是一种特殊的POM文件,我们可以在其中集中应用程序的依赖项。在大多数情况下,这些依赖性可以很好地工作,应用作集合,就像我们在春季使用的BOM中看到的那样。

SBOM是您在应用程序旁边创建的东西,因此任何用户或客户端都有一种统一的方法来找出您的应用程序在引擎盖下使用的内容。

我为什么要创建一个SBOM?

创建SBOM的原因有多种。首先,您可以创建有关应用程序包含的内容的透明度。在大多数Java应用中,生产的二进制文件中有80%至90%由其他Java包组成,例如库和框架。

如今,我们看到了很多security issues in the supply chain。您使用的依赖项是供应链的一部分,因此,如果在其中一个库中发现问题,则需要知道应用程序是否易受伤害。以最近的Log4ShellSpring4Shell漏洞为例,某些常用包装受到损害。当提供SBOM作为每个版本的一部分时,最终用户和客户可以轻松检查漏洞是否影响它们。

当您提供软件时,预计SBOM的创建将是常见的实践,有时甚至是强制性的。因此,我们认为重要的是要涵盖如何为您的Java项目创建这些SBOM,我们在本文的其余部分中介绍了这些SBOM。

SBOM标准:SPDX和Cyclonedx

当前,SBOM有多个标准。最常用的两个是SPDX和Cyclonedx。这两种标准都提供了一种显示您应用程序包含的组件的方法。

软件包数据交换(SPDX)是一个Linux Foundation协作项目,为传播软件材料清单的开放标准,包括出处,许可,安全性和其他相关信息。 SPDX规范被认为是ISO/IEC 5962:2021的国际安全性,许可符号和其他软件供应链工件的国际开放标准。

Cyclonedx是OWASP基金会的SBOM标准,旨在用于应用程序安全环境和供应链组件分析,提供了所有第一方和第三方软件组件的清单。该规范是丰富的,并且超出了软件库的范围,例如诸如软件材料清单(Saasbom),脆弱性可利用性交换(VEX)等标准。 Cyclonedx项目提供了XML,JSON和协议缓冲区的标准,以及与标准创建或互操作的大型collection of official and community-supported tools

何时在Java中创建SBOM

Java是一种编译语言,因此每当您构建应用程序的发行版本时,都应创建SBOM。因此,在使用Java构建系统之一时创建SBOM很有意义,因为您的构建系统下载了您需要编译和构建应用程序的所有软件包。通过使用Maven或Gradle的插件,您可以在单台计算机上或作为CI Pipeline的一部分

的一部分轻松地创建SBOM

用Maven创建Java SBOM

cyclonedx插件

Maven Central和Github上有一个CylConedx插件,似乎已维护良好且常用。

<plugins>
   <plugin>
       <groupId>org.cyclonedx</groupId>
       <artifactId>cyclonedx-maven-plugin</artifactId>
       <version>2.7.1</version>
       <executions>
           <execution>
               <phase>package</phase>
               <goals>
                   <goal>makeAggregateBom</goal>
               </goals>
           </execution>
       </executions>
       <configuration>
           <projectType>library</projectType>
           <schemaVersion>1.4</schemaVersion>
           <includeBomSerialNumber>true</includeBomSerialNumber>
           <includeCompileScope>true</includeCompileScope>
           <includeProvidedScope>true</includeProvidedScope>
           <includeRuntimeScope>true</includeRuntimeScope>
           <includeSystemScope>true</includeSystemScope>
           <includeTestScope>false</includeTestScope>
           <includeLicenseText>false</includeLicenseText>
           <outputReactorProjects>true</outputReactorProjects>
           <outputFormat>all</outputFormat>
           <outputName>CycloneDX-Sbom</outputName>
       </configuration>
   </plugin>
</plugins>

您可以以不同的方式配置Cyclonedx插件。在这种情况下,我将插件的makeAggregateBom目标绑定到Maven的包装阶段。创建我的罐子后,插件将创建一个SBOM,考虑到聚合。它不包括测试依赖项,并在我的目标文件夹中释放XML和JSON格式的SBOM。

在SBOM中,单独提及所有依赖性,无论是直接还是及时的。在这种情况下,jackson-databind软件包通过sprint-boot-starter-web进行了传输。

<component type="library" bom-ref="pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.4?type=jar">
 <publisher>FasterXML</publisher>
 <group>com.fasterxml.jackson.core</group>
 <name>jackson-databind</name>
 <version>2.13.4</version>
 <description>General data-binding functionality for Jackson: works on core streaming API</description>
 <hashes>
   <hash alg="MD5">03cb7aea126610e4c96ca6d14d75cc55</hash>
   <hash alg="SHA-1">98b0edfa8e4084078f10b7b356c300ded4a71491</hash>
   <hash alg="SHA-256">c9faff420d9e2c7e1e4711dbeebec2506a32c9942027211c5c293d8d87807eb6</hash>
   <hash alg="SHA-512">23f32026b181c6c71efc7789a8420c7d5cbcfb15f7696657e75f9cbe3635d13a88634b5db3c344deb914b719d60e3a9bfc1b63fa23152394e1e70b8e7bcd2116</hash>
   <hash alg="SHA-384">e25e844575891b2f3bcb2fdc67ae9fadf54d2836052c9ea2c045f1375eaa97e4780cd6752bef0ebc658fa17400c55268</hash>
   <hash alg="SHA3-384">e6955877c2c27327f6814f06d681118be2ae1a36bc5ff2e84ad27f213203bf77c347ba18d9abc61d5f1c99b6e81f6c2d</hash>
   <hash alg="SHA3-256">88b12b0643a4791fa5cd0c5e30bc2631903870cf916c8a1b4198c856fd91e5f4</hash>
   <hash alg="SHA3-512">7e86a69bcf7b4c8a6949acce0ec15f33b74d5ac604f23cd631ec16bfdfd70d42499028b9d062648b31d7a187ea4dc98ec296a329f4cfd4952744ed1281fa9d9a</hash>
 </hashes>
 <licenses>
   <license>
     <id>Apache-2.0</id>
   </license>
 </licenses>
 <purl>pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.4?type=jar</purl>
 <externalReferences><reference type="vcs"><url>http://github.com/FasterXML/jackson-databind</url></reference><reference type="website"><url>http://fasterxml.com/</url></reference><reference type="distribution"><url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url></reference></externalReferences>
</component>

Maven的SPDX插件(原型)

对于SPDX,也有一个Maven plugin。但是,这仍然标记为原型。在下面的示例中,我使用了最新版本(在编写时),其配置与GitHub Readme中提到的相似。此外,我将SPDX创建任务绑定到软件包阶段,类似于Cyclonedx示例。

<plugin>
   <groupId>org.spdx</groupId>
   <artifactId>spdx-maven-plugin</artifactId>
   <version>0.6.1</version>
   <executions>
       <execution>
           <id>build-spdx</id>
           <phase>package</phase>
           <goals>
               <goal>createSPDX</goal>
           </goals>
       </execution>
   </executions>
</plugin>

默认情况下,此版本的插件的输出位于/target/site/{groupId}_{artifactId}-{version}.spdx.json中。正如文件扩展名已经建议的那样,默认输出为JSON。

浏览输出,令我惊讶的是,它仅包含顶级依赖项,而不包含瞬态。现在,此插件被标记为原型,因此可能就是原因。此外,我可能做错了。但是,阅读文档并没有给我一个明确的提示。

SPDX CLI工具

另外,有命令行工具可用,称为spdx-sbom-generator。该CLI工具可以为许多软件包管理人员(包括用于Java应用程序的Maven)生成SPDX SBOM。 Gradle目前不支持。

从命令行调用此工具,没有我应用程序的根部没有任何参数,可以在SPDX格式中为我创建一个SBOM。其他输出也通过使用参数来支持。

./spdx-sbom-generator

这个生成的SBOM似乎在我认为应该的那样具有所有的传递依赖性。

##### Package representing the jackson-databind

PackageName: jackson-databind
SPDXID: SPDXRef-Package-jackson-databind-2.13.4
PackageVersion: 2.13.4
PackageSupplier: Organization: jackson-databind
PackageDownloadLocation: https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind/2.13.4
FilesAnalyzed: false
PackageChecksum: SHA1: 7d03e73aa50d143b3ecbdea2c0c9e158e5ed8021
PackageHomePage: NOASSERTION
PackageLicenseConcluded: NOASSERTION
PackageLicenseDeclared: NOASSERTION
PackageCopyrightText: NOASSERTION
PackageLicenseComments: NOASSERTION
PackageComment: NOASSERTION

Relationship: SPDXRef-Package-jackson-databind-2.13.4 DEPENDS_ON SPDXRef-Package-jackson-annotations-2.13.4
Relationship: SPDXRef-Package-jackson-databind-2.13.4 DEPENDS_ON SPDXRef-Package-jackson-core-2.13.4

如果要以SPDX格式创建SBOM,我建议通过原型插件进行此工具。

与Gradle创建Java SBOM

现在让我们看一下Gradle。虽然Gradle的使用少于Maven,但仍然使用了大量使用,我们绝对可以说它是生态系统中一种良好的构建工具。

Gradle的Cyclonedx

有一个可用于Gradle的Cyconedx插件。就像我们之前讨论的Maven插件一样,CycloneDX organization on Github释放了Gradle插件,其中一些与Maven插件相同的维护器。

要使用插件,只需将其添加到gradle文件中的插件块中:

plugins {
   id 'org.cyclonedx.bom' version '1.7.2'
}

您可以使用像下面的cyclonedxBom块一起配置插件

cyclonedxBom {
   includeConfigs = ["runtimeClasspath"]
   skipConfigs = ["compileClasspath", "testCompileClasspath"]
   projectType = "application"
   schemaVersion = "1.4"
   destination = file("build/reports")
   outputName = "CycloneDX-Sbom"
   outputFormat = "all"
   includeBomSerialNumber = true
   componentVersion = "2.0.0"
}

在此示例中,我还在Gradle文件末尾添加了Line build.finalizedBy('cyclonedxBom')。现在,它将在构建我的应用程序后自动调用cyclonedxBom目标,并且与Maven插件相似。显然,这取决于您以及如何连接插件目标。

输出是预期的,与我们在Maven插件中看到的相似。在上面显示的配置中,您将在项目的build文件夹中找到SBOM的JSON和XML输出。因此,此插件是Gradle用户创建SBOMS

的绝佳选择。

SPDX for Gradle

不幸的是,我们找不到真正的插件来为Gradle项目创建SPDX型SBOM。此外,第三方CLI工具要么不可用,要么无法正确地用于基于Gradle的Java项目。因此,目前,没有简单的方法来为Gradle生成SPDX SBOM。

为您的Java项目创建SBOM

当您构建Java项目时,建立SBOM似乎很快就会变得更加受欢迎。让您的构建系统照顾这一点很有意义。

对于Maven和Gradle,都可以使用插件,可在构建应用程序时创建SBOM。正如我们上面显示的那样,使用这些插件来创建SBOM与您的Java构建工件很简单。

加强您的软件供应链安全

SNYK可帮助您确保软件供应链的关键组件,包括开源库,容器图像,云基础架构和开发人员工具。

Learn more