Skip to main content

Como criar SBOMs em Java com Maven e Gradle

Escrito por
blog hero software supply chain security

31 de outubro de 2022

0 minutos de leitura

Ao desenvolver aplicações em Java, dependemos muito de bibliotecas e frameworks externos. E cada pacote Java importado provavelmente também depende de outras bibliotecas. Isso significa que muitas vezes não há transparência sobre a quantidade de pacotes Java incluídos na aplicação. Para quem desenvolve, essas dependências aninhadas (transitivas) trazem um problema: provavelmente você não sabe quais são todas as bibliotecas que está usando de fato.

Recentemente, falamos sobre por que e como devemos gerenciar nossas dependências com cuidado. No artigo Boas práticas para gerenciar dependências Java, apresentei as opções e ferramentas disponíveis para definir uma estratégia de gerenciamento de dependências. Mas e quando você entrega sua aplicação Java a um cliente? Como ele sabe quais dependências estão incluídas? E, mais importante, como pode verificar se elas não apresentam vulnerabilidades de segurança? A resposta é uma lista de materiais de software.

O que é uma SBOM?

Uma lista de materiais de software, geralmente chamada de SBOM, é uma relação de todos os componentes de software usados em uma aplicação. A SBOM inclui bibliotecas de código aberto de terceiros, pacotes fornecidos por fornecedores e artefatos próprios desenvolvidos pela organização. Basicamente, você pode pensar nela como uma lista completa dos ingredientes da sua aplicação.

Mas cuidado para não confundir SBOM com a Bill Of Materials (BOM) do Maven. No Maven, uma BOM é um tipo especial de arquivo POM que permite centralizar as dependências de uma aplicação. Na maioria dos casos, essas dependências funcionam bem juntas e devem ser usadas em conjunto, como vemos nas BOMs do Spring.

A SBOM é criada junto com a aplicação para que qualquer usuário ou cliente tenha uma maneira padronizada de descobrir o que ela usa nos bastidores.

Por que devo criar uma SBOM?

Há vários motivos para criar uma SBOM. Para começar, ela traz transparência sobre o que compõe sua aplicação. Na maioria das aplicações Java, de 80% a 90% do binário gerado consiste em outros pacotes Java, como bibliotecas e frameworks.

Hoje em dia, vemos muitos problemas de segurança na cadeia de suprimentos. As dependências que você usa fazem parte da sua cadeia de suprimentos. Portanto, se for encontrado um problema em uma dessas bibliotecas, você precisa saber se uma aplicação está vulnerável. Veja as vulnerabilidades recentes Log4Shell e Spring4Shell, que afetaram alguns pacotes de uso comum. Quando uma SBOM é incluída em cada versão, usuários finais e clientes podem verificar facilmente se foram afetados por vulnerabilidades.

Espera-se que a criação de SBOMs se torne uma prática comum — e, às vezes, até obrigatória — na entrega de software. Por isso, consideramos importante explicar como criar SBOMs para seu projeto Java. É o que faremos no restante deste artigo.

Padrões de SBOM: SPDX e CycloneDX

Atualmente, há vários padrões de SBOM. Os dois mais usados são SPDX e CycloneDX. Ambos permitem apresentar os componentes que sua aplicação contém.

O Software Package Data Exchange (SPDX) é um projeto colaborativo da Linux Foundation que oferece um padrão aberto para comunicar informações de listas de materiais de software, incluindo procedência, licenciamento, segurança e outros dados relacionados. A especificação SPDX é reconhecida como padrão internacional aberto para segurança, conformidade de licenças e outros artefatos da cadeia de suprimentos de software, conforme a norma ISO/IEC 5962:2021.

CycloneDX é um padrão de SBOM da fundação OWASP, desenvolvido para contextos de segurança de aplicações e análise de componentes da cadeia de suprimentos. Ele oferece um inventário de todos os componentes de software próprios e de terceiros. A especificação é abrangente e vai além das bibliotecas de software, incluindo padrões como lista de materiais de software como serviço (SaaSBOM), Vulnerability Exploitability Exchange (VEX) e outros. O projeto CycloneDX oferece padrões em XML, JSON e Protocol Buffers, além de uma ampla coleção de ferramentas oficiais e mantidas pela comunidade que criam ou são interoperáveis com o padrão.

Quando criar uma SBOM em Java

Java é uma linguagem compilada, então você deve criar uma SBOM sempre que gerar uma versão de lançamento da aplicação. Por isso, faz muito sentido criar uma SBOM usando um dos sistemas de build de Java: o sistema baixa todos os pacotes necessários para compilar e gerar sua aplicação. Com um plugin para Maven ou Gradle, você pode criar SBOMs facilmente a cada lançamento do binário, seja em uma única máquina ou como parte do pipeline de CI.

Como criar uma SBOM Java com Maven

Plugin CycloneDX para Maven

Há um plugin CycloneDX disponível no Maven Central e no GitHub que parece ser bem mantido e bastante usado.

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

Você pode configurar o plugin CycloneDX de várias maneiras. Neste caso, vinculei o objetivo makeAggregateBom do plugin à fase package do Maven. Depois que meu JAR é criado, o plugin gera uma SBOM, considerando a agregação. Ele exclui as dependências de teste e gera a SBOM nos formatos XML e JSON na pasta target.

Todas as dependências, diretas e transitivas, são listadas individualmente na SBOM, como no exemplo abaixo. Neste caso, o pacote jackson-databind foi incluído de forma transitiva na minha aplicação por meio de 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>

Plugin SPDX para Maven (protótipo)

Também há um plugin do Maven para SPDX. No entanto, ele ainda está marcado como protótipo. No exemplo abaixo, usei a versão mais recente (na época da redação deste artigo), com uma configuração semelhante à descrita no README do GitHub. Também vinculei a tarefa de criação da SPDX à fase package, como no exemplo do 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>

Por padrão, esta versão do plugin salva a saída em /target/site/{groupId}_{artifactId}-{version}.spdx.json. Como a extensão do arquivo já indica, o formato padrão da saída é JSON.

Ao analisar a saída, me surpreendi ao ver que ela continha apenas as dependências de nível superior, e não as transitivas. O plugin está marcado como protótipo, então talvez esse seja o motivo. Também é possível que eu esteja fazendo algo errado. No entanto, a documentação não me deu uma indicação clara.

Ferramenta de linha de comando SPDX para Maven

Outra opção é a ferramenta de linha de comando spdx-sbom-generator. Ela pode gerar SBOMs SPDX para vários gerenciadores de pacotes, incluindo Maven para aplicações Java. No momento, o Gradle não é compatível.

Executar essa ferramenta pela linha de comando, sem parâmetros, na raiz da minha aplicação gera uma SBOM no formato SPDX. Outros formatos de saída, como JSON, também são compatíveis quando se usa um parâmetro.

./spdx-sbom-generator

Essa SBOM gerada parece listar todas as dependências transitivas individualmente, como eu esperava.

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

Se você quiser criar SBOMs no formato SPDX, recomendo essa ferramenta em vez do plugin protótipo.

Como criar uma SBOM Java com Gradle

Agora, vamos falar do Gradle. Embora seja menos usado que o Maven, o Gradle ainda tem ampla adoção e é, sem dúvida, uma ferramenta de build consolidada no ecossistema.

CycloneDX para Gradle

Há um plugin CycloneDX para Gradle. Assim como o plugin Maven que vimos antes, o plugin para Gradle é publicado pela organização CycloneDX no GitHub e conta com alguns dos mesmos mantenedores do plugin Maven.

Para usar o plugin, basta adicioná-lo ao bloco de plugins no arquivo Gradle:

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

Você pode configurar o plugin com um bloco cyclonedxBom, como no exemplo abaixo:

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

Neste exemplo, também adicionei a linha build.finalizedBy('cyclonedxBom') ao final do arquivo Gradle. Assim, o destino cyclonedxBom será chamado automaticamente após o build da aplicação, funcionando de maneira semelhante ao plugin Maven. É claro que cabe a você decidir se e como quer vincular o destino do plugin.

A saída é a esperada e semelhante à que vimos com o plugin Maven. Com a configuração acima, você encontrará as versões JSON e XML da SBOM na pasta build do projeto. Portanto, esse plugin é uma ótima opção para quem usa Gradle e quer criar SBOMs.

SPDX para Gradle

Infelizmente, não encontramos um plugin de fato para criar SBOMs no formato SPDX em projetos Gradle. As ferramentas de linha de comando de terceiros também não estão disponíveis ou não funcionam corretamente com projetos Java baseados em Gradle. Por enquanto, não há uma maneira fácil de gerar SBOMs SPDX para Gradle.

Como criar SBOMs para seus projetos Java

Criar uma SBOM durante o build do projeto Java parece ser uma prática que logo ficará mais popular. Faz muito sentido deixar essa tarefa por conta do sistema de build.

Há plugins para Maven e Gradle que criam SBOMs durante o build da aplicação. Como mostramos acima, esses plugins facilitam a criação de SBOMs junto com os artefatos de build Java.

Comece a participar de desafios de Capture the Flag

Aprenda a resolver desafios de Capture the Flag assistindo sob demanda ao nosso workshop virtual introdutório.

Leia mais

blog feature snyk container custom base recommendations
Blog

Conheça o Evo Agentic AppSec: segurança que acompanha o ritmo do desenvolvimento com IA

O desenvolvimento impulsionado por IA está avançando mais rápido do que os fluxos de trabalho de segurança tradicionais. O AppSec agentivo da Snyk usa agentes de segurança para encontrar, corrigir e verificar vulnerabilidades continuamente, enquanto as pessoas definem os critérios.

blog feature ai violet
Blog

VulnBump: agentes de IA conseguem corrigir dependências vulneráveis sem quebrar o build?

O VulnBump testa agentes de IA em 307 atualizações reais de dependências. Uma correção só é considerada válida se eliminar a vulnerabilidade e mantiver o build e os testes funcionando.

Blog

Modelos de ponta encontraram as vulnerabilidades. Só o atacante encontrou as cadeias.

A análise estática encontrou as falhas, mas só os testes de ataque em aplicações ativas provaram como elas poderiam ser encadeadas para causar invasões. Uma comparação entre Evo COS, Claude Security e Claude Code Security.