Skip to main content

SBOMs in Java mit Maven und Gradle erstellen

Artikel von
blog hero software supply chain security

31. Oktober 2022

0 Min. Lesezeit

Bei der Entwicklung von Java-Anwendungen sind wir stark auf externe Bibliotheken und Frameworks angewiesen. Und jedes importierte Java-Paket hängt wahrscheinlich seinerseits von weiteren Bibliotheken ab. Dadurch ist oft nicht wirklich transparent, wie viele Java-Pakete in Ihrer Anwendung enthalten sind. Als Entwickler stehen Sie vor dem Problem, dass Sie aufgrund dieser verschachtelten (transitiven) Abhängigkeiten vermutlich nicht alle tatsächlich verwendeten Bibliotheken kennen.

Vor Kurzem haben wir darüber gesprochen, warum und wie wir unsere Abhängigkeiten sorgfältig verwalten sollten. Im Artikel Best Practices für die Verwaltung von Java-Abhängigkeiten habe ich die verfügbaren Optionen und Tools für die Einrichtung einer Strategie zur Abhängigkeitsverwaltung vorgestellt. Doch was ist, wenn Sie Ihre Java-Anwendung an einen Kunden ausliefern? Woher weiß dieser, welche Abhängigkeiten enthalten sind? Und noch wichtiger: Wie kann er prüfen, ob die Abhängigkeiten keine Sicherheitslücken aufweisen? Die Antwort ist eine Software-Stückliste.

Was ist eine SBOM?

Eine Software-Stückliste, häufig als SBOM abgekürzt, ist eine Liste aller Softwarekomponenten, die in einer Anwendung verwendet werden. Eine SBOM umfasst Open-Source-Bibliotheken von Drittanbietern, von Anbietern bereitgestellte Pakete und organisationsintern entwickelte Artefakte. Sie können sie sich als vollständige Zutatenliste Ihrer Anwendungen vorstellen.

Verwechseln Sie eine SBOM jedoch nicht mit der Bill Of Materials (BOM) von Maven. In Maven ist eine BOM eine besondere Art von POM-Datei, in der wir Abhängigkeiten für eine Anwendung zentral verwalten können. In den meisten Fällen funktionieren diese Abhängigkeiten gut zusammen und sollten als Paket verwendet werden – wie bei den BOMs für Spring.

Eine SBOM erstellen Sie zusätzlich zu Ihrer Anwendung. So können Nutzer und Kunden einheitlich herausfinden, welche Komponenten im Hintergrund in Ihrer Anwendung verwendet werden.

Warum sollte ich eine SBOM erstellen?

Es gibt mehrere Gründe, eine SBOM zu erstellen. Zunächst schaffen Sie Transparenz darüber, was Ihre Anwendung enthält. Bei den meisten Java-Anwendungen bestehen 80 bis 90 % der erzeugten Binärdatei aus anderen Java-Paketen wie Bibliotheken und Frameworks.

Heutzutage treten zahlreiche Sicherheitsprobleme in der Supply Chain auf. Die von Ihnen verwendeten Abhängigkeiten sind Teil Ihrer Supply Chain. Wird also ein Problem in einer dieser Bibliotheken entdeckt, müssen Sie wissen, ob eine Anwendung gefährdet ist. Denken Sie an die kürzlich bekannt gewordenen Sicherheitslücken Log4Shell und Spring4Shell, bei denen bestimmte weit verbreitete Pakete kompromittiert wurden. Wird mit jeder Version eine SBOM bereitgestellt, können Endnutzer und Kunden leicht prüfen, ob sie von Sicherheitslücken betroffen sind.

Es ist davon auszugehen, dass die Erstellung von SBOMs bei der Auslieferung von Software zur gängigen Praxis wird oder mitunter sogar verpflichtend ist. Deshalb halten wir es für wichtig, zu zeigen, wie Sie SBOMs für Ihr Java-Projekt erstellen. Darum geht es im restlichen Artikel.

SBOM-Standards: SPDX und CycloneDX

Derzeit gibt es mehrere SBOM-Standards. Am häufigsten werden SPDX und CycloneDX verwendet. Beide Standards bieten eine Möglichkeit, die Komponenten Ihrer Anwendung aufzulisten.

Der Software Package Data Exchange (SPDX) ist ein Gemeinschaftsprojekt der Linux Foundation. Es bietet einen offenen Standard für den Austausch von Informationen zu Software-Stücklisten, darunter Angaben zu Herkunft, Lizenzierung, Sicherheit und weiteren relevanten Themen. Die SPDX-Spezifikation ist als internationaler offener Standard für Sicherheit, Lizenzkonformität und weitere Artefakte der Software-Supply-Chain nach ISO/IEC 5962:2021 anerkannt.

CycloneDX ist ein SBOM-Standard der OWASP Foundation, der für Anwendungssicherheit und die Analyse von Supply-Chain-Komponenten entwickelt wurde. Er bietet eine Bestandsaufnahme aller internen und externen Softwarekomponenten. Die umfassende Spezifikation geht über Softwarebibliotheken hinaus und umfasst auch Standards wie die Software as a Service Bill of Materials (SaaSBOM), den Vulnerability Exploitability Exchange (VEX) und weitere. Das CycloneDX-Projekt stellt Standards in XML, JSON und Protocol Buffers sowie eine umfangreiche Sammlung offizieller und von der Community unterstützter Tools bereit, mit denen sich der Standard erstellen oder nutzen lässt.

Wann sollte eine SBOM für Java erstellt werden?

Java ist eine kompilierte Sprache. Deshalb sollten Sie bei jedem Build einer Release-Version Ihrer Anwendung eine SBOM erstellen. Es bietet sich also an, die SBOM mit einem der Java-Build-Systeme zu erstellen, denn Ihr Build-System lädt alle Pakete herunter, die Sie zum Kompilieren und Erstellen Ihrer Anwendung benötigen. Mit einem Maven- oder Gradle-Plug-in können Sie bei jeder Veröffentlichung Ihrer Binärdatei ganz einfach eine SBOM erstellen – entweder auf einem einzelnen Rechner oder als Teil Ihrer CI-Pipeline.

Eine Java-SBOM mit Maven erstellen

CycloneDX-Plug-in für Maven

Im Maven Central Repository und auf GitHub ist ein CycloneDX-Plug-in verfügbar, das offenbar gut gepflegt und weit verbreitet ist.

<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>

Sie können das CycloneDX-Plug-in auf verschiedene Arten konfigurieren. In diesem Beispiel habe ich das Ziel makeAggregateBom des Plug-ins an die Package-Phase von Maven gebunden. Nachdem mein JAR erstellt wurde, erzeugt das Plug-in eine SBOM und berücksichtigt dabei die Aggregation. Testabhängigkeiten werden ausgeschlossen. Die SBOM wird sowohl im XML- als auch im JSON-Format im Ordner „target“ abgelegt.

Alle Abhängigkeiten, sowohl direkte als auch transitive, werden in der SBOM einzeln aufgeführt, wie unten zu sehen ist. Das Paket jackson-databind war in diesem Fall über sprint-boot-starter-web transitiv in meiner Anwendung enthalten.

<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>

SPDX-Plug-in für Maven (Prototyp)

Auch für SPDX gibt es ein Maven-Plug-in. Es ist allerdings noch als Prototyp gekennzeichnet. Im folgenden Beispiel habe ich die zum Zeitpunkt der Erstellung aktuelle Version mit einer ähnlichen Konfiguration wie in der GitHub-README verwendet. Wie beim CycloneDX-Beispiel habe ich außerdem die Aufgabe zur Erstellung der SPDX-Datei an die Package-Phase gebunden.

<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>

Bei dieser Plug-in-Version befindet sich die Ausgabe standardmäßig unter /target/site/{groupId}_{artifactId}-{version}.spdx.json. Wie die Dateiendung bereits vermuten lässt, erfolgt die Ausgabe standardmäßig im JSON-Format.

Beim Durchsehen der Ausgabe war ich überrascht, dass sie nur die direkten Abhängigkeiten und nicht die transitiven enthielt. Das Plug-in ist noch als Prototyp gekennzeichnet, was der Grund dafür sein könnte. Möglicherweise mache ich auch etwas falsch. In der Dokumentation fand ich jedoch keinen eindeutigen Hinweis.

SPDX-CLI-Tool für Maven

Alternativ gibt es ein Befehlszeilentool namens spdx-sbom-generator. Mit diesem CLI-Tool lassen sich SPDX-SBOMs für viele Paketmanager erstellen, darunter auch für Maven in Java-Anwendungen. Gradle wird derzeit nicht unterstützt.

Wenn Sie das Tool ohne Parameter über die Befehlszeile im Stammverzeichnis Ihrer Anwendung aufrufen, erstellt es eine SBOM im SPDX-Format. Weitere Ausgabeformate wie JSON lassen sich ebenfalls über einen Parameter festlegen.

./spdx-sbom-generator

Diese generierte SBOM scheint wie erwartet alle transitiven Abhängigkeiten einzeln aufzuführen.

##### 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

Wenn Sie SBOMs im SPDX-Format erstellen möchten, würde ich dieses Tool dem Prototyp-Plug-in vorziehen.

Eine Java-SBOM mit Gradle erstellen

Sehen wir uns nun Gradle an. Gradle wird zwar seltener als Maven verwendet, kommt aber dennoch häufig zum Einsatz und ist im Ökosystem ein etabliertes Build-Tool.

CycloneDX für Gradle

Für Gradle ist ein CycloneDX-Plug-in verfügbar. Wie das zuvor besprochene Maven-Plug-in wird auch das Gradle-Plug-in von der CycloneDX-Organisation auf Github veröffentlicht. Einige Maintainer sind dieselben wie beim Maven-Plug-in.

Um das Plug-in zu verwenden, fügen Sie es einfach dem Plugin-Block Ihrer Gradle-Datei hinzu:

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

Sie können das Plug-in mit einem cyclonedxBom-Block wie unten konfigurieren:

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"
}

In diesem Beispiel habe ich außerdem am Ende meiner Gradle-Datei die Zeile build.finalizedBy('cyclonedxBom') hinzugefügt. Dadurch wird nach dem Build meiner Anwendung automatisch das Ziel cyclonedxBom aufgerufen und das Plug-in verhält sich ähnlich wie das Maven-Plug-in. Natürlich können Sie selbst entscheiden, ob und wie Sie das Plug-in-Ziel einbinden möchten.

Die Ausgabe entspricht den Erwartungen und ähnelt der des Maven-Plug-ins. Mit der oben gezeigten Konfiguration finden Sie die SBOM sowohl im JSON- als auch im XML-Format im build-Ordner Ihres Projekts. Das Plug-in ist daher eine ausgezeichnete Option für Gradle-Nutzer, die SBOMs erstellen möchten.

SPDX für Gradle

Leider konnten wir kein geeignetes Plug-in finden, um SPDX-SBOMs für Gradle-Projekte zu erstellen. Auch Drittanbieter-CLI-Tools sind entweder nicht verfügbar oder funktionieren bei Java-Projekten auf Gradle-Basis nicht richtig. Daher gibt es derzeit keine einfache Möglichkeit, SPDX-SBOMs für Gradle zu generieren.

SBOMs für Ihre Java-Projekte erstellen

Eine SBOM beim Build Ihres Java-Projekts zu erstellen, dürfte bald immer gängiger werden. Diese Aufgabe Ihrem Build-System zu überlassen, ist sinnvoll.

Für Maven und Gradle sind Plug-ins verfügbar, die beim Build Ihrer Anwendung SBOMs erstellen. Wie oben gezeigt, können Sie mit diesen Plug-ins ganz unkompliziert SBOMs zusammen mit Ihren Java-Build-Artefakten erstellen.

Starten Sie mit Capture the Flag

Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Herausforderungen lösen.