In this article
Cómo proteger una API de Java Spring Boot frente a la vulnerabilidad de serialización de JSONObject CVE-2023-5072
El reciente descubrimiento de CVE-2023-5072, una vulnerabilidad crítica relacionada con la serialización de JSONObject, ha puesto de manifiesto importantes problemas de seguridad para los desarrolladores de Java, en especial para quienes usan el framework Spring Boot.
Esta vulnerabilidad de agotamiento de memoria, causada por una falla en la forma en que JSONObject maneja la serialización, puede exponer las aplicaciones Java a diversas amenazas de seguridad.
CVE-2023-5072 representa un escenario complejo en el que la integridad de las estructuras de datos puede verse comprometida durante los procesos de serialización y deserialización. Esto es especialmente importante para las aplicaciones creadas con Spring Boot, que se usa ampliamente por su eficiencia y facilidad para desarrollar aplicaciones Java robustas.
Comprender la naturaleza y las implicaciones de estas vulnerabilidades es esencial para mantener la seguridad y confiabilidad de las aplicaciones Java en un entorno digital cada vez más complejo.
En este artículo, presentamos una descripción clara del problema. Para ello, creamos nuestra propia API de Java sencilla y ofrecemos pasos prácticos para mitigar los riesgos en un entorno de API de Spring Boot.
Una API de Java Spring Boot vulnerable a JSONObject CVE-2023-5072
Para demostrar la vulnerabilidad de serialización JSON en JSONObject, crearemos un pequeño servidor de API de Java con Spring Boot y Maven para gestionar una solicitud POST a /api/todos, analizar un objeto JSON con una matriz de tareas pendientes y devolver su cantidad en la respuesta HTTP.
Comencemos con las instrucciones paso a paso para crear este proyecto.
Paso 1: Configurar el proyecto
1. Crear un nuevo proyecto de Spring Boot:
a. Puedes usar Spring Initializr para generar tu proyecto.
b. Elige Maven como herramienta de compilación.
c. Agrega 'Spring Web' como dependencia.
d. Descarga y descomprime el proyecto.

2. Abrir el proyecto:
a. Abre el proyecto en tu IDE preferido (como IntelliJ IDEA, Eclipse o VSCode).
b. Confirma que tienes el archivo pom.xml en su lugar, con la dependencia de Spring Boot y una entrada de compilación para spring-boot-maven-plugin.
Paso 2: Agregar dependencias de Java
Cómo administrar las dependencias en pom.xml
En un archivo pom.xml de Maven, una popular herramienta de compilación de Java, las dependencias (bibliotecas o módulos externos que necesita tu proyecto) se especifican con una combinación de groupId, artifactId y version. En conjunto, estos elementos identifican de forma única una versión específica de una biblioteca en los repositorios de Maven.
Edita el archivo pom.xml y asegúrate de tener la dependencia de Spring Web. Agrega la dependencia de la biblioteca JSONObject. Si no está incluida en Spring Boot, puedes usar la biblioteca JSON de org.json y agregar esta dependencia:
xml
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20230618</version>
</dependency>
Veamos qué significa cada parte, con especial atención a artifactId:
groupId: Por lo general, es el nombre de dominio invertido de la organización o el grupo que creó la biblioteca. En nuestra aplicación de ejemplo,org.jsones el groupId, lo que indica que la biblioteca está mantenida por la organización json.org.
artifactId: Es el nombre de la biblioteca o el módulo. Por lo general, es el nombre que se usa para referirse a una biblioteca. En nuestro caso, el artifactId esjson, es decir, la biblioteca que agregamos se llama simplemente "json". Es común que el artifactId refleje la funcionalidad principal o la identidad de la biblioteca.
version: Especifica la versión concreta de la biblioteca que quieres usar. En nuestro ejemplo, 20230618 es un identificador único de versión para la biblioteca json. El control de versiones es crucial para garantizar compilaciones consistentes, ya que las versiones nuevas de las bibliotecas pueden incluir cambios incompatibles con tu proyecto.
Por lo tanto, en el archivo pom.xml, la declaración de dependencia le indica a Maven que descargue e incluya en el proyecto la biblioteca json, que forma parte del grupo org.json, específicamente la versión 20230618. La herramienta de compilación Maven obtendrá automáticamente esta biblioteca de un repositorio central y la dejará disponible para el proyecto.
Paso 3: Crear el controlador de la API de Java
En esta parte agregamos nuestro propio código Java al proyecto y presentamos una API nueva. Este nuevo endpoint de API define una solicitud HTTP POST a la URI /api/todos, atendida por la aplicación Java.
Crear una nueva clase de controlador:
En tu proyecto, ve a
src/main/java/<your-group-id>/<your-artifact-id>/y crea un directorio nuevo llamadocontroller/. Si seguiste las opciones predeterminadas de Spring Initializr, esta ruta seríasrc/main/java/com/example/demo/controller/DemoApplication.java.Crea un archivo de clase Java llamado
TodoController.java.
Agregar el código del controlador:
Anota la clase con
@RestController.Crea un método para gestionar la solicitud POST.
A continuación, se muestra un ejemplo completo del archivo de clase 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;
}
}Paso 4: Ejecutar el servidor
Ejecutemos el servidor de API Java Spring Boot. Podemos hacerlo de dos maneras principales:
Usa el IDE para ejecutar la aplicación, normalmente con la tecla F5.
Ve al directorio raíz del proyecto en la terminal y ejecuta el comando
mvn spring-boot:run. Para usar este método, debes tener Maven instalado en tu entorno de desarrollo.
Ahora estamos listos para probar el endpoint de la API de Java y enviar una solicitud POST. Usa una herramienta como Postman o cURL para enviar una solicitud POST a http://localhost:8080/api/todos. El cuerpo debe ser una cadena JSON, por ejemplo: {"todos": ["Task 1", "Task 2"]}.
Este es un ejemplo del comando cURL que puedes usar para enviar la solicitud POST:
sh
curl -X POST http://localhost:8080/api/todos \
-H "Content-Type: application/json" \
-d '{"todos": ["Task 1", "Task 2", "Task 3"]}'La respuesta 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: 3Prueba el proyecto Java Spring Boot con Snyk
En el desarrollo de Java, garantizar la seguridad y la integridad de tus aplicaciones es tan crucial como su funcionalidad. Una herramienta eficaz para este propósito es Snyk, una plataforma de seguridad especializada en identificar y corregir vulnerabilidades en las dependencias de los proyectos.
Los desarrolladores de Java suelen preferir métodos que se adapten mejor a sus flujos de trabajo actuales. Veamos cómo probar una aplicación Java con Snyk como complemento de Maven.
Integra Snyk con tu proyecto Maven
Snyk ofrece un complemento de Maven que puedes agregar a tu pom.xml. Este complemento te permite ejecutar pruebas de Snyk como parte del proceso de compilación de Maven.
Agrega el complemento de Maven de Snyk a tu pom.xml:
xml
<plugin>
<groupId>io.snyk</groupId>
<artifactId>snyk-maven-plugin</artifactId>
<version>2.2.0</version>
</plugin>Después de configurar el complemento, deberás autenticarlo con tu cuenta de Snyk. Regístrate en Snyk si aún no lo has hecho; crear una cuenta en el sitio web de Snyk es gratis.
Luego, puedes generar un token de API de Snyk desde la configuración de tu cuenta de Snyk y definirlo como una variable de entorno SNYK_TOKEN en tu máquina o entorno de CI/CD. Para que esté disponible en la CLI y ejecutar la prueba de Snyk para Maven, podemos ejecutar:
sh
export SNYK_TOKEN=<the Snyk API token goes here>También puedes proporcionar una configuración explícita para el complemento de Snyk y establecer apiToken y los argumentos de línea de comandos de forma más específica. Esta configuración del complemento de Maven de Snyk se agrega a la declaración <plugin>:
xml
<configuration>
<apiToken>${env.SNYK_TOKEN}</apiToken>
<args>
<arg>--all-projects</arg>
</args>
</configuration>Luego podemos ejecutar la prueba de Snyk:
sh
mvn snyk:testVaya, parece que Snyk encontró vulnerabilidades en la nueva dependencia JSONObject que agregamos. Observa el siguiente registro de la prueba de Maven de Snyk durante el proceso de compilación:
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 y defensor de la seguridad en Snyk, escribió una publicación detallada en el blog sobre cómo usar Snyk con el complemento de Maven.
Demostrar el error de agotamiento de memoria en JSONObject
Snyk encontró un tipo de vulnerabilidad de denegación de servicio en el paquete del analizador JSON y proporcionó más información para consultar este problema de seguridad en la base de datos de vulnerabilidades de Snyk.
Allí se muestra una prueba de concepto que podemos aplicar para entender por qué nuestro servidor de API de Java es vulnerable. Apliquemos la entrada vulnerable al analizador JSON para ver qué sucede.
Vuelve al código de TodoController.java y agrega el siguiente método privado que genera una carga JSON malformada:
java
private static String makeNested(int depth) {
if (depth == 0) {
return "{\"a\":1}";
}
return "{\"a\":1;\t\0" + makeNested(depth - 1) + ":1}";
}A continuación, actualiza el método de ruta de la API handleTodo() para usar esa cadena como entrada del constructor JSONObject:
java
String vulnerablePayload = makeNested(30);
JSONObject jsonData = new JSONObject(vulnerablePayload);Vuelve a ejecutar el servidor de API de Java con mvn spring-boot:run, envía de nuevo la solicitud HTTP POST de cURL y observa cómo el servidor se pausa un rato hasta que devuelve una respuesta. Durante esta pausa, se produce una fuga de memoria y la JVM ocupa cada vez más memoria, hasta que la aplicación Java termina bloqueándose.
Snyk ya nos indicó cómo corregir el problema de seguridad. Si vuelves a ejecutar la compilación de Maven snyk:test, verás que Snyk explica cómo corregir esta vulnerabilidad de denegación de servicio: actualizar de la versión 20230618 a la versión 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@20230618Por lo tanto, debes editar el archivo pom.xml para actualizar la versión de la dependencia org.json a 20231013 y corregir el problema de seguridad.
Recursos adicionales para escribir, compilar y distribuir aplicaciones Java seguras
Puedes consultar el código fuente completo que se usó para crear la API Java Spring Boot con la dependencia JSONObject en el siguiente repositorio: https://github.com/snyk-snippets/spring-boot-java-jsonobject-vulnerability
Si mantienes altos estándares para tu código Java y te comprometes a lanzar productos de alta calidad, te recomiendo consultar los siguientes recursos para aprender más sobre los aspectos de seguridad de las aplicaciones Java:
Guía de referencia de 10 prácticas recomendadas de seguridad para Java, de Brian Vermeer y Jim Manico
Artículo de Brian Vermeer sobre las prácticas recomendadas para administrar dependencias de Java
Análisis detallado de la serialización y deserialización en Java: explicación de la vulnerabilidad de deserialización de Java
Comienza a usar Snyk para el desarrollo seguro en Java.
El complemento de Maven de Snyk es de código abierto y está disponible aquí: https://github.com/snyk/snyk-mvn-plugin
Visita Snyk Learn para aprender sobre temas de seguridad de aplicaciones Java
Explora el estado de la seguridad del código abierto
Conoce las tendencias y los enfoques actuales sobre el software de código abierto y la seguridad de la cadena de suministro.