Skip to main content

Como lidar com vulnerabilidades de segurança no Spring Boot

Escrito por
feature open source

29 de novembro de 2023

0 minutos de leitura

No desenvolvimento de software, gerenciar dependências é uma parte essencial da criação de aplicações robustas e seguras. O Spring Boot, muito usado por desenvolvedores Java, facilita a criação de aplicações, mas há mais do que parece à primeira vista. Manter as dependências sob controle é fundamental para garantir que seus projetos Spring Boot funcionem sem problemas e continuem resilientes diante de ameaças em constante evolução.

Um aspecto fundamental do gerenciamento de dependências no Spring Boot é a segurança. Vulnerabilidades de software são descobertas com frequência. Manter as dependências do seu projeto atualizadas é como vestir uma armadura de segurança digital. Dependências desatualizadas podem ser como portas destrancadas, convidando possíveis ameaças a entrar — e queremos evitar isso.

Como encontrar vulnerabilidades no Spring Boot

As ferramentas de análise de composição de software (SCA) são muito úteis para desenvolvedores. Elas oferecem uma solução simples para gerenciar dependências, lidar com vulnerabilidades de segurança, navegar por questões de licenciamento e garantir a conformidade dos seus projetos de software. Ao usar Snyk Open Source como sua ferramenta de SCA, você pode descobrir facilmente se os pacotes do seu Spring Boot contêm vulnerabilidades.

No meu projeto de exemplo, encontrei várias vulnerabilidades. A primeira é um problema de segurança de alta severidade em `netty-codec-http2`. Essa é uma dependência transitiva trazida por `spring-boot-start-webflux`, como você pode ver abaixo.

Detalhes da vulnerabilidade em io.netty:netty-codec-http2, identificada como uma negação de serviço com pontuação de 725 e recomendação de atualização para corrigir o problema.

A segunda vulnerabilidade que quero abordar é um problema de execução arbitrária de código de severidade média, introduzido pelo pacote `snakeyaml`. Essa também é uma dependência transitiva, desta vez trazida por `spring-boot-starter-security`.

Relatório de vulnerabilidade de org.yaml:snakeyaml mostrando execução arbitrária de código, pontuação 405, introduzida pelo Spring Boot Security 2.7.16.

Hoje, não vou entrar nos detalhes de cada vulnerabilidade, mas você pode conferir o artigo dedicado para saber mais sobre o problema do `snakeyaml`. Vamos nos concentrar em resolver o problema e implementar a melhor solução.

Como corrigir pacotes vulneráveis na sua aplicação Spring Boot

Para a primeira vulnerabilidade, há uma correção bem definida. Minha aplicação é baseada no Spring Boot 2.7.16 e, portanto, o `spring-boot-starter-webflux` também está na versão 2.7.16.

Atualizar o starter Webflux para a versão 2.7.17 deve resolver o problema. Há várias maneiras

de fazer isso, mas nem todas são recomendadas.

Normalmente, o manifesto do seu Spring Boot é parecido com o código a seguir:

Maven:

<parent>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-parent</artifactId>
   <version>2.7.16</version>
   <relativePath/>
</parent>
…
<dependencies>
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-security</artifactId>
   </dependency>
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-webflux</artifactId>
   </dependency>
  …
</dependencies>

Gradle:

plugins {
 …
 id 'org.springframework.boot' version '2.7.16'
 id 'io.spring.dependency-management' version '1.1.3'
}
…
dependencies {
 implementation 'org.springframework.boot:spring-boot-starter-security'
 implementation 'org.springframework.boot:spring-boot-starter-webflux'
 …
}

Atualize o starter do Spring Boot

O número de versão de cada `spring-boot-starters` é definido pelo `spring-boot-starter-parent` (Maven) ou pelo plugin `org.springframework.boot` no Gradle. 

Isso acontece principalmente porque todos esses pacotes starter fazem parte da mesma versão do Spring e são testados em conjunto.

Ao analisar a primeira vulnerabilidade encontrada no meu projeto, a sugestão de correção é atualizar `spring-boot-starter-webflux` para a versão 2.7.17, resolvendo a vulnerabilidade de segurança de alta severidade. Isso sugere que basta corrigir o pacote Webflux, especificando uma versão, como no exemplo abaixo.

Maven:

   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-webflux</artifactId>
       <version>2.7.17</version>
   </dependency>

Gradle:

implementation 'org.springframework.boot:spring-boot-starter-webflux:2.7.17'

Mas pare por aqui! Essa não é a maneira recomendada de resolver o problema. A versão específica 2.7.17 é usada no lugar da versão definida pelo parent. Embora isso possa funcionar por se tratar de uma versão de patch, não é a solução mais adequada nem a mais segura. Como o semver não garante que as APIs permaneçam intactas, não podemos ter certeza de que isso vai funcionar. Mas o argumento mais importante é que provavelmente já existe uma nova versão completa do Spring Boot.

Lembra que eu disse que esses starters precisam funcionar em conjunto? Por isso, a melhor opção é atualizar toda a distribuição do Spring Boot para a versão 2.7.17. Isso é ainda mais importante ao atualizar para versões minor ou major de um pacote, que podem causar incompatibilidades na API e impedir que os componentes funcionem bem juntos. Então, neste exemplo, atualize o parent (Maven) ou o plugin (Gradle), em vez dos starters individualmente.

Maven:

<parent>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-parent</artifactId>
   <version>2.7.17</version>
   <relativePath/>
</parent>

Gradle:

plugins {
 …
 id 'org.springframework.boot' version '2.7.17'
}

Recomendo que você vá além e atualize toda a versão do Spring Boot para a versão mais recente compatível. Para descobrir qual é, basta acessar https://start.spring.io.

Interface do Spring Initializr exibindo as opções de projeto, linguagem e versão do Spring Boot, com Gradle-Groovy, Java e Spring Boot 2.7.17 selecionados.

Atualização de dependências transitivas

No segundo caso, o Snyk identificou que não há uma recomendação clara de correção na minha aplicação. No momento, também não há uma versão disponível de um `spring-boot-starter` sem essa dependência transitiva insegura. O que sabemos é que existe uma versão atualizada da dependência transitiva. Atualizar para o `snakeyaml` 2.0 vai resolver o problema.

Mais uma vez, vale ressaltar que este é apenas um exemplo. Talvez já exista uma versão atualizada de um `spring-boot-starter` quando você ler este artigo, então não deixe de confirmar. Para saber mais sobre a vulnerabilidade do SnakeYaml, confira nosso artigo dedicado sobre o assunto.

Atualização do parâmetro de versão

Primeiro, verifique se a versão do pacote que você quer atualizar é uma propriedade do Spring Boot. A documentação do Spring Boot inclui um apêndice com as versões das dependências de cada versão específica. Você também encontra lá as propriedades de versão. Essas propriedades podem ser sobrescritas no Maven e no Gradle para usar uma versão mais recente da dependência transitiva. 

No Maven, você pode adicionar uma propriedade à seção de propriedades.

<properties>
   <snakeyaml.version>3.0</snakeyaml.version>
</properties>

No Gradle, podemos editar essa propriedade em um arquivo `gradle.properties`:

snakeyaml.version=3.0

Essa é a opção preferida em relação ao gerenciamento de dependências, pois `snakeyaml` é apenas uma biblioteca. No entanto, em muitos casos, há mais de uma biblioteca. A versão pode se referir a um BOM (bill of materials), que não deve ser confundido com um SBOM. Um BOM representa um conjunto de grupos de pacotes que precisam ser usados em conjunto e, essencialmente, ter a mesma versão — como um pacote de API e outro de implementação. Ambos precisam estar na mesma versão para funcionar como esperado.

Declaração de gerenciamento de dependências

Outra opção é usar os mecanismos da sua ferramenta de build para atualizar dependências transitivas. Essa alternativa só deve ser usada se você não puder atualizar a propriedade de versão conforme mostrado na seção anterior.

No Maven, isso costuma ser adicionado ao bloco `dependencyManagement`. Esse bloco garante que a versão atualizada indicada nele seja usada sempre que a biblioteca for incluída como dependência transitiva.

Maven:

<dependencyManagement>
   <dependencies>
       <dependency>
           <groupId>org.yaml</groupId>
           <artifactId>snakeyaml</artifactId>
           <version>2.2</version>
       </dependency>
   </dependencies>
</dependencyManagement>

Como usamos o plugin dependency-management do Spring no nosso arquivo Gradle, temos recursos bem semelhantes disponíveis para o Gradle. Esse plugin foi inserido pelo inicializador do Spring Boot ao gerar a estrutura inicial do meu projeto.

Gradle:

dependencyManagement {
   dependencies {
       dependency 'org.yaml:snakeyaml:2.2'
   }
}

Se você não usa o plugin `io.spring.dependency-management`, também pode adicionar constraints a dependências transitivas no Gradle para atualizar uma dependência como a do exemplo abaixo. Lembre-se de que essas constraints não funcionam bem com o plugin `dependency-management` do Spring. Portanto, você deve usar um ou outro.

dependencies {
    …
    constraints {
        implementation('org.yaml:snakeyaml:2.2') {
            because 'previous versions have a security issue'
        }
    …
}

Analise suas aplicações Spring Boot com o Snyk

Analisar sua aplicação Spring Boot com o Snyk é essencial para garantir a segurança e a estabilidade do seu software. O Snyk ajuda a identificar vulnerabilidades nas dependências da sua aplicação, que agentes mal-intencionados podem explorar se não forem corrigidas. Depois de ler este artigo, você também já sabe qual é a melhor maneira de aplicar as recomendações de correção fornecidas pelo Snyk.

Analisar sua base de código regularmente ajuda a tratar problemas de segurança de forma proativa e reduz o risco de vazamentos de dados e outros incidentes de segurança. Por isso, inclua as análises no seu fluxo de trabalho e saiba como agir quando uma vulnerabilidade aparecer.

Abaixo, analisei minha aplicação com o Snyk CLI antes e depois da correção. Optei por atualizar a versão geral do Spring Boot e a propriedade de versão específica do `snakeyaml`.

Antes:

Saída do terminal mostrando os resultados da análise do Snyk das dependências do Spring Boot, incluindo 14 problemas e 21 caminhos vulneráveis.

Depois:

A saída do terminal informa que 98 dependências foram testadas, 3 problemas foram encontrados e há 6 caminhos vulneráveis, incluindo vulnerabilidades de RCE e de validação de certificados.

Então, lembre-se: quando encontrar no Snyk uma vulnerabilidade relacionada a uma dependência da sua aplicação Spring Boot, siga estas etapas:

  • Atualize a versão geral do Spring Boot em vez de atualizar um starter específico.

  • Ao atualizar a versão do Spring Boot, não basta atualizar as propriedades de versão das dependências do Spring Boot.

  • Use o gerenciamento de dependências no Maven ou no Gradle para atualizar dependências transitivas específicas.

Comece a resolver 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

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.

feature insights context
Blog

Os ataques autônomos já chegaram. A defesa precisa acompanhar o ritmo.

Os atacantes autônomos estão reduzindo o tempo disponível para a defesa. Saiba como a descoberta, a correção, a validação e a prevenção contínuas ajudam as equipes de segurança a acompanhar esse ritmo.

Blog

Por que agentes de programação com IA continuam criando falhas de controle de acesso

Agentes de programação com IA podem gerar uma lógica de autorização que compila e passa pela revisão, mas permite que um tenant acesse os dados de outro. Saiba por que é difícil detectar falhas de controle de acesso e como evitá-las.