При создании приложений на Java мы сильно зависим от внешних библиотек и фреймворков. И каждый импортируемый пакет Java, скорее всего, также зависит от большего количества библиотек. Это означает, что количество Java‑пакетов, включенных в ваше приложение, часто не совсем прозрачно.

Эти вложенные (транзитивные) зависимости создают проблему, заключающуюся в том, что вы как разработчик, вероятно, не знаете всех библиотек, которые на самом деле используете.

Недавно мы обсуждали, почему и как мы должны тщательно поддерживать наши зависимости. В статье  Лучшие практики управления зависимостями Java я рассказал о доступных вариантах и инструментах для настройки стратегии управления зависимостями.

Но что, если вы передаете свое Java-приложение клиенту?

Как они узнают, какие зависимости включены?

Что еще важнее, как они могут проверить, не уязвимы ли эти зависимости для проблем безопасности?

Ответом является спецификация программного обеспечения SBOM (software bill of materials).

Что такое SBOM?

Спецификация  программного обеспечения, часто сокращенно называемая SBOM, представляет собой список всех программных компонентов, используемых в приложении. SBOM состоит из сторонних библиотек с открытым исходным кодом, пакетов, предоставленных поставщиками, и собственных артефактов, созданных организацией. В принципе SBOM можно рассматривать как полный список ингредиентов для ваших приложений.

Но будьте осторожны, не путайте SBOM с Bill Of Materials (BOM) в Maven. В Maven BOM — это особый вид POM-файла, в котором мы можем централизовать зависимости для приложения. В большинстве случаев эти зависимости хорошо работают вместе и должны использоваться как набор, как мы видим в BOM, используемых в Spring.

SBOM — это то, что вы создаете рядом с вашим приложением, чтобы любой пользователь или клиент имел единый способ узнать, что ваше приложение использует под капотом.

Почему я должен создавать SBOM?

Существует несколько причин для создания SBOM. Прежде всего, вы обеспечиваете прозрачность о том, что содержит ваше приложение. В большинстве Java‑приложений от 80% до 90% создаваемого бинарного файла состоит из других Java‑пакетов, таких, как библиотеки и фреймворки.

В настоящее время мы видим много проблем с безопасностью в цепочке поставок. Зависимости, которые вы используете, являются частью вашей цепочки поставок, поэтому, если в одной из этих библиотек обнаруживается проблема, вам необходимо знать, уязвимо ли приложение.

Возьмем недавние уязвимости Log4Shell и Spring4Shell, из‑за которых были скомпрометированы некоторые широко используемые пакеты. Когда SBOM предоставляется как часть каждого выпуска, конечные пользователи и клиенты могут легко проверить, затрагивают ли их уязвимости.

Ожидается, что создание SBOM станет обычной практикой, а иногда даже обязательной, при поставке программное обеспечение. Поэтому мы считаем важным рассказать, как создавать SBOM для вашего Java‑проекта, о чем мы расскажем в оставшейся части этой статьи.

Стандарты SBOM: SPDX и CycloneDX

В настоящее время существует несколько стандартов для SBOM. Двумя наиболее часто используемыми являются SPDX и CycloneDX. Оба эти стандарта обеспечивают способ отображения компонентов, которые содержит ваше приложение.

Software Package Data Exchange (SPDX) — это совместный проект Linux Foundation, который обеспечивает открытый стандарт для передачи информации о программных пакетах, включая информацию о происхождении, лицензировании, безопасности и другую связанную информацию.

Спецификация SPDX признана международным открытым стандартом безопасности, лицензионного соответствия и других артефактов цепочки поставок программного обеспечения в качестве ISO/IEC 5962:2021.

CycloneDX — это стандарт SBOM от фонда OWASP, разработанный для контекстов безопасности приложений и анализа компонентов цепочки поставок, обеспечивающий инвентаризацию всех компонентов программного обеспечения как собственных, так и сторонних производителей.

Спецификация обширна и выходит за рамки библиотек программного обеспечения и включает такие стандарты, как спецификация программного обеспечения как услуги (SaaSBOM), Vulnerability Exploitability Exchange (VEX) и многое другое. Проект CycloneDX предоставляет стандарты XML, JSON и Protocol Buffers, а также большую  коллекцию официальных и поддерживаемых сообществом инструментов, которые создают или взаимодействуют со стандартом.

Когда создавать SBOM в Java

Java — компилируемый язык, поэтому вам следует создавать SBOM всякий раз, когда вы собираете релизную версию своего приложения.

Поэтому создание SBOM при использовании одной из систем сборки Java имеет большой смысл, поскольку ваша система сборки загружает все пакеты, необходимые для компиляции и сборки вашего приложения.

Используя плагин для Maven или Gradle, вы можете легко создавать SBOM с каждым выпуском вашего бинарного файла либо на отдельной машине, либо как часть вашего конвейера CI.

Создание SBOM для Java с помощью Maven

Плагин CycloneDX для Maven

На 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. После создания моего JAR плагин создаст SBOM с учетом агрегации. Он исключает тестовые зависимости и публикует SBOM в формате XML и JSON в моей целевой папке.

Все зависимости, как прямые, так и транзитивные, упоминаются в 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>

Плагин SPDX для Maven (прототип)

Для SPDX также существует плагин Maven. Однако он все еще обозначен как прототип. В приведенном ниже примере я использовал последнюю версию (на момент написания статьи) с с конфигурацией, аналогичной указанной в 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 для Maven

В качестве альтернативы существует инструмент командной строки под названием  spdx-sbom-generator. Этот инструмент CLI может генерировать SPDX SBOM для многих менеджеров пакетов, включая Maven для Java-приложений. Gradle в настоящее время не поддерживается.

Вызов этого инструмента из командной строки без каких-либо параметров в корне моего приложения создает для меня SBOM в формате SPDX. Другие выходные данные, такие как JSON, также поддерживаются с помощью параметра.

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

Если вы хотите создавать SBOM в формате SPDX, я бы посоветовал этот инструмент вместо плагина-прототипа.

Создание Java SBOM с помощью Gradle

Теперь давайте посмотрим на Gradle. Хотя Gradle используется реже, чем Maven, он используется в значительной степени, и мы можем определенно сказать, что это хорошо принятый инструмент сборки в экосистеме Java.

CycloneDX для Gradle

Для Gradle существует плагин CycloneDX. Как и плагин для Maven, о котором мы говорили ранее, плагин для Gradle выпускается организацией  CycloneDX на Github  с частью из тех же сопровождающих, что и для плагина 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"
}

В этом примере я также добавил строку build.finalizedBy('cyclonedxBom') в конец моего файла Gradle. 

Теперь он будет автоматически вызывать цель cyclonedxBom после сборки моего приложения и вести себя аналогично плагину Maven. Очевидно, это зависит от вас, если и как вы хотите подключить цель плагина.

Выходные данные соответствуют ожиданиям и похожи на то, что мы видели с плагином Maven. С конфигурацией, показанной выше, вы найдете выходные данные SBOM в формате JSON и XML в папке build вашего проекта. Таким образом, этот плагин является отличным вариантом для пользователей Gradle для создания SBOM.

SPDX для Gradle

К сожалению, мы не смогли найти настоящий плагин для создания SPDX-типа SBOM для проектов Gradle. Кроме того, сторонние инструменты CLI либо недоступны, либо некорректно работают с Java-проектами на базе Gradle.

Таким образом, на данный момент не существует простого способа создания SPDX SBOMs для Gradle.

Создание SBOM для ваших Java-проектов

Создание SBOM во время сборки Java-проекта кажется практикой, которая скоро станет более популярной.

Позволить вашей системе сборки позаботиться об этом имеет большой смысл.

Как для Maven, так и для Gradle доступны плагины, которые создают SBOM во время сборки вашего приложения.

Создание SBOM вместе с вашими артефактами сборки Java с помощью этих плагинов очень просто, как мы показали выше.

Комментарии (1)


  1. sshikov
    07.04.2023 15:41
    +2

    ваша система сборки загружает все пакеты, необходимые для компиляции и сборки вашего приложения

    Но это же неправда. Загружать в рантайме можно что угодно, не говоря уже о том, что и компилировать в рантайме в целом тоже можно. Ну скажем, мавеновский artefact resolver под названием Aether, он вполне доступен как компонент, и может применяться для динамической загрузки артефактов, а дальше можно добиться включения их в класс лоадер.

    Короче, я не против SBOM, но далеко не всегда это возможно статически.