In this article
Como proteger uma API Java Spring Boot contra a vulnerabilidade de serialização do JSONObject CVE-2023-5072
A descoberta recente da CVE-2023-5072, uma vulnerabilidade crítica relacionada à serialização do JSONObject, trouxe à tona importantes preocupações de segurança para desenvolvedores Java, especialmente aqueles que usam o framework Spring Boot.
Essa vulnerabilidade de esgotamento de memória, causada por uma falha na forma como o JSONObject lida com a serialização, pode expor aplicações Java a diversas ameaças de segurança.
A CVE-2023-5072 representa um cenário desafiador, no qual a integridade das estruturas de dados pode ser comprometida durante os processos de serialização e desserialização. Isso é especialmente importante para aplicações desenvolvidas com Spring Boot, muito usado pela eficiência e facilidade para criar aplicações Java robustas.
Entender a natureza e as implicações de vulnerabilidades como essa é essencial para manter a segurança e a confiabilidade das aplicações Java em um cenário digital cada vez mais complexo.
Neste artigo, vamos apresentar uma visão clara do problema, criando uma API Java simples e mostrando medidas práticas para reduzir os riscos em um ambiente Spring Boot.
Uma API Java Spring Boot vulnerável à CVE-2023-5072 do JSONObject
Para demonstrar a vulnerabilidade de serialização JSON no JSONObject, vamos criar um pequeno servidor de API Java com Spring Boot e Maven para receber uma solicitação POST em /api/todos, analisar um objeto JSON com uma lista de tarefas e retornar a quantidade delas na resposta HTTP.
Vamos começar com as instruções passo a passo para criar esse projeto.
Etapa 1: configure o projeto
1. Crie um novo projeto Spring Boot:
a. Use o Spring Initializr para gerar seu projeto.
b. Escolha Maven como ferramenta de build.
c. Adicione 'Spring Web' como dependência.
d. Baixe e descompacte o projeto.

2. Abra o projeto:
a. Abra o projeto no IDE de sua preferência, como IntelliJ IDEA, Eclipse ou VSCode.
b. Confira se o arquivo pom.xml está no lugar e contém a dependência do Spring Boot e uma entrada de build para o spring-boot-maven-plugin.
Etapa 2: adicione as dependências Java
Como gerenciar dependências no pom.xml?
Em um arquivo pom.xml usado pelo Maven, uma ferramenta popular de build para Java, as dependências (bibliotecas ou módulos externos necessários ao projeto) são especificadas com uma combinação de groupId, artifactId e version. Juntos, esses elementos identificam de forma exclusiva uma versão específica de uma biblioteca nos repositórios do Maven.
Edite o arquivo pom.xml e confira se ele contém a dependência Spring Web. Adicione a dependência da biblioteca JSONObject. Se ela não estiver incluída no Spring Boot, você poderá usar a biblioteca JSON de org.json adicionando esta dependência:
xml
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20230618</version>
</dependency>
Vamos explicar cada parte, com foco no artifactId:
groupId: em geral, é o nome de domínio invertido da organização ou do grupo que criou a biblioteca. No exemplo da nossa aplicação,org.jsoné o groupId, indicando que a biblioteca é mantida pela organização json.org.
artifactId: é o nome da biblioteca ou do módulo em si. É como geralmente nos referimos a uma biblioteca. No nosso caso, o artifactId éjson, ou seja, o nome da biblioteca que estamos adicionando é simplesmente "json". É comum que o artifactId indique a principal funcionalidade ou identidade da biblioteca.
version: especifica a versão da biblioteca que você quer usar. No nosso exemplo, 20230618 é um identificador exclusivo da versão da biblioteca json. O controle de versões é essencial para garantir builds consistentes, já que versões mais recentes das bibliotecas podem trazer mudanças incompatíveis com seu projeto.
Assim, no arquivo pom.xml, a declaração da dependência informa ao Maven que ele deve baixar e incluir no projeto a biblioteca json, do grupo org.json, especificamente na versão 20230618. A ferramenta de build Maven buscará automaticamente essa biblioteca em um repositório central e a disponibilizará para uso no projeto.
Etapa 3: crie o controlador da API Java
Nesta etapa, vamos adicionar nosso próprio código Java ao projeto e criar uma nova API. Esse endpoint define uma solicitação HTTP POST para o URI /api/todos, atendido pela aplicação Java.
Crie uma nova classe de controlador:
No projeto, acesse
src/main/java/<your-group-id>/<your-artifact-id>/e crie um diretório chamadocontroller/. Se você seguiu as opções padrão do Spring Initializr, o caminho serásrc/main/java/com/example/demo/controller/DemoApplication.java.Crie um novo arquivo de classe Java chamado
TodoController.java.
Adicione o código do controlador:
Anote a classe com
@RestController.Crie um método para tratar a solicitação POST.
Veja abaixo um exemplo completo do arquivo de classe TodoController.java:
java
import org.json.JSONObject;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TodoController {
@PostMapping("/api/todos")
public String handleTodo(@RequestBody String todoData) {
JSONObject jsonObject = new JSONObject(todoData);
int count = jsonObject.getJSONArray("todos").length();
return "Count: " + count;
}
}Etapa 4: execute o servidor
Vamos executar o servidor da API Java Spring Boot. Há duas maneiras principais de fazer isso:
Use o IDE para executar a aplicação, geralmente pressionando a tecla F5.
No terminal, acesse o diretório raiz do projeto e execute o comando
mvn spring-boot:run. Para usar esse método, o Maven precisa estar instalado no seu ambiente de desenvolvimento.
Agora podemos testar o endpoint da API Java e enviar uma solicitação POST. Use uma ferramenta como Postman ou cURL para enviar uma solicitação POST a http://localhost:8080/api/todos. O corpo deve ser uma string JSON, por exemplo: {"todos": ["Task 1", "Task 2"]}.
Veja um exemplo do comando cURL que você pode usar para enviar a solicitação POST:
sh
curl -X POST http://localhost:8080/api/todos \
-H "Content-Type: application/json" \
-d '{"todos": ["Task 1", "Task 2", "Task 3"]}'A resposta HTTP será:
sh
< HTTP/1.1 200
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 8
< Date: Mon, 27 Nov 2023 10:23:51 GMT
<
* Connection #0 to host localhost left intact
Count: 3Teste o projeto Java Spring Boot com a Snyk
No desenvolvimento Java, garantir a segurança e a integridade das aplicações é tão importante quanto garantir seu funcionamento. Uma ferramenta eficaz para isso é a Snyk, uma plataforma de segurança especializada em identificar e corrigir vulnerabilidades nas dependências dos projetos.
Desenvolvedores Java costumam preferir métodos mais alinhados aos fluxos de trabalho que já usam. Vamos ver como testar uma aplicação Java com a Snyk como plugin do Maven.
Integre a Snyk ao seu projeto Maven
A Snyk oferece um plugin do Maven que você pode adicionar ao seu pom.xml. Com ele, você pode executar testes da Snyk como parte do processo de build do Maven.
Adicione o plugin do Maven da Snyk ao seu pom.xml:
xml
<plugin>
<groupId>io.snyk</groupId>
<artifactId>snyk-maven-plugin</artifactId>
<version>2.2.0</version>
</plugin>Depois de configurar o plugin, você precisará autenticá-lo com sua conta da Snyk. Crie uma conta na Snyk, se ainda não tiver uma. O cadastro é gratuito no site da Snyk.
Em seguida, gere um token de API da Snyk nas configurações da sua conta e defina-o como a variável de ambiente SNYK_TOKEN na sua máquina ou no ambiente de CI/CD. Para disponibilizá-lo na CLI e executar o teste da Snyk com o Maven, execute:
sh
export SNYK_TOKEN=<the Snyk API token goes here>Você também pode configurar o plugin da Snyk explicitamente, definindo o apiToken e os argumentos de linha de comando de forma mais específica. Essa configuração do plugin do Maven da Snyk é adicionada à declaração <plugin>:
xml
<configuration>
<apiToken>${env.SNYK_TOKEN}</apiToken>
<args>
<arg>--all-projects</arg>
</args>
</configuration>Em seguida, podemos executar o teste da Snyk:
sh
mvn snyk:testParece que a Snyk encontrou vulnerabilidades na nova dependência JSONObject que adicionamos. Confira o log a seguir do teste da Snyk com o Maven durante o processo de build:
sh
$ mvn snyk:test
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------------< com.example:demo >--------------------------
[INFO] Building demo 0.0.1-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- snyk:2.2.0:test (default-cli) @ demo ---
[INFO] Snyk Executable Path: /Users/lirantal/Library/Application Support/Snyk/snyk-macos
[INFO] Snyk CLI Version: 1.1254.0
[INFO]
[INFO] Testing /Users/lirantal/projects/repos/spring-boot-java-jsonobject-vulnerability...
[INFO]
[INFO] Tested 37 dependencies for known issues, found 1 issue, 1 vulnerable path.
[INFO]
[INFO]
[INFO] Issues to fix by upgrading:
[INFO]
[INFO] Upgrade org.json:json@20230618 to org.json:json@20231013 to fix
[INFO] ✗ Allocation of Resources Without Limits or Throttling [High Severity][https://security.snyk.io/vuln/SNYK-JAVA-ORGJSON-5962464] in org.json:json@20230618
[INFO] introduced by org.json:json@20230618
[INFO]
[INFO]
[INFO]
[INFO] Organization: snyk-demo-567
[INFO] Package manager: maven
[INFO] Target file: pom.xml
[INFO] Project name: com.example:demo
[INFO] Open source: no
[INFO] Project path: /Users/lirantal/projects/repos/spring-boot-java-jsonobject-vulnerability
[INFO] Licenses: enabled
[INFO]
[INFO]
[ERROR]
[ERROR] You have reached your monthly limit of 401 private tests for your snyk-demo-567 org.
[ERROR] To learn more about our plans and increase your tests limit visit https://snyk.io/plans.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 12.507 s
[INFO] Finished at: 2023-11-27T14:42:01+02:00
[INFO] ------------------------------------------------------------------------Brian Vermeer, Java Champion e defensor da segurança na Snyk, escreveu uma publicação detalhada no blog sobre como usar a Snyk com o plugin do Maven.
Demonstre o bug de esgotamento de memória no JSONObject
A Snyk encontrou um tipo de vulnerabilidade de negação de serviço no pacote do analisador JSON e forneceu mais informações para consultar esse problema de segurança no banco de dados de vulnerabilidades da Snyk.
Lá, há uma prova de conceito que podemos usar para entender por que nosso servidor de API Java está vulnerável. Vamos passar a entrada vulnerável para o analisador JSON e ver o que acontece.
Volte ao código TodoController.java e adicione o seguinte método privado, que gera um payload JSON malformado:
java
private static String makeNested(int depth) {
if (depth == 0) {
return "{\"a\":1}";
}
return "{\"a\":1;\t\0" + makeNested(depth - 1) + ":1}";
}Em seguida, atualize o método da rota de API handleTodo() para usar essa string como entrada do construtor JSONObject:
java
String vulnerablePayload = makeNested(30);
JSONObject jsonData = new JSONObject(vulnerablePayload);Execute novamente o servidor da API Java com mvn spring-boot:run, envie outra vez a solicitação HTTP POST com cURL e observe que o servidor fica pausado por um tempo antes de retornar uma resposta. Durante essa pausa, ocorre um vazamento de memória e a JVM passa a ocupar cada vez mais memória, até que a aplicação Java falha.
A Snyk já indicou como corrigir esse problema de segurança. Se você executar novamente o build Maven snyk:test, verá que a Snyk recomenda corrigir essa vulnerabilidade de negação de serviço atualizando a versão 20230618 para a versão 20231013:
sh
[INFO] Tested 37 dependencies for known issues, found 1 issue, 1 vulnerable path.
[INFO]
[INFO]
[INFO] Issues to fix by upgrading:
[INFO]
[INFO] Upgrade org.json:json@20230618 to org.json:json@20231013 to fix
[INFO] ✗ Allocation of Resources Without Limits or Throttling [High Severity][https://security.snyk.io/vuln/SNYK-JAVA-ORGJSON-5962464] in org.json:json@20230618
[INFO] introduced by org.json:json@20230618Portanto, edite o arquivo pom.xml e atualize a versão da dependência org.json para 20231013 para corrigir o problema de segurança.
Materiais complementares para desenvolver, compilar e lançar aplicações Java seguras
Você pode consultar o código-fonte completo usado para criar a API Java Spring Boot com a dependência JSONObject neste repositório: https://github.com/snyk-snippets/spring-boot-java-jsonobject-vulnerability
Se você segue padrões rigorosos para seu código Java e tem o compromisso de lançar produtos de alta qualidade, recomendamos estes materiais para saber mais sobre os aspectos de segurança das aplicações Java:
Guia de referência 10 práticas recomendadas de segurança em Java, de Brian Vermeer e Jim Manico
Artigo de Brian Vermeer: Práticas recomendadas para gerenciar dependências Java
Uma análise aprofundada de serialização e desserialização em Java: entenda a vulnerabilidade de desserialização do Java
Comece a usar a Snyk para o desenvolvimento seguro em Java.
O plugin do Maven da Snyk é de código aberto e está disponível aqui: https://github.com/snyk/snyk-mvn-plugin
Acesse o Snyk Learn para aprender sobre segurança de aplicações Java
Conheça o cenário atual da segurança de código aberto
Entenda as tendências e abordagens atuais para proteger softwares de código aberto e a cadeia de suprimentos.