Securing a Java Spring Boot API from broken JSONObject serialization CVE-2023-5072
The recent discovery of CVE-2023-5072, a critical vulnerability related to JSONObject serialization, has brought to light significant security concerns for Java developers, particularly those utilizing the Spring Boot framework.
This out-of-memory vulnerability, stemming from a flaw in the way JSONObject handles serialization, can potentially expose Java applications to a range of security threats.
CVE-2023-5072 represents a challenging scenario where the integrity of data structures during serialization and deserialization processes may be compromised. This is especially crucial for applications built with Spring Boot, which is widely used for its efficiency and ease in developing robust Java applications.
Understanding the nature and implications of such vulnerabilities is essential for maintaining the security and reliability of Java applications in an increasingly complex digital landscape.
In this article, we aim to provide a clear overview of this issue by building our own simple Java API and offer practical steps to mitigate the risks in a Spring Boot API environment.
A Spring Boot Java API vulnerable to JSONObject CVE-2023-5072
To demonstrate the JSON serialization vulnerability in JSONObject we will create a small Java API server using Spring Boot and Maven to handle a POST request at /api/todos
, parse a JSON object with an array of To-Do items, and return their count in the HTTP response.
Let’s begin with step-by-step instructions to build this project.
Step 1: Set up the project
1. Create a New Spring Boot Project:
a. You can use Spring Initializr to generate your project.
b. Choose Maven as the build tool.
c. Add 'Spring Web' as a dependency.
d. Download and unzip the project.
2. Open the Project:
a. Open the project in your preferred IDE (like IntelliJ IDEA, Eclipse, or VSCode).
b. Confirm you have the pom.xml
file in place with the Spring Boot dependency and a build entry for the spring-boot-maven-plugin
.
Step 2: Add Java dependencies
How to manage dependencies in pom.xml?
In a pom.xml
file used by Maven, which is a popular Java build tool, dependencies (external libraries or modules your project needs) are specified using a combination of groupId, artifactId, and version. These elements together uniquely identify a specific version of a library in the Maven repositories.
Edit the pom.xml
file and ensure you have the Spring Web dependency. Add the JSONObject library dependency. If it's not included in Spring Boot, you can use the JSON library from org.json by adding this dependency:
xml
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20230618</version>
</dependency>
Let's break down what each part means, focusing on the artifactId:
groupId
: This is generally the reverse domain name of the organization or group that created the library. In our example application,org.json
is the groupId, indicating that the library is maintained by the organization json.org.
artifactId
: This is the name of the actual library or module. It's what you would generally refer to when talking about a library. In our case, the artifactId isjson
, which means the name of the library we are adding is simply referred to as "json". It's a common practice for the artifactId to reflect the primary functionality or identity of the library.
version
: This specifies the particular version of the library that you want to use. The version 20230618 in our example is a unique version identifier for the json library. Versioning is crucial for ensuring consistent builds, as newer versions of libraries might introduce changes that are not compatible with your project.
So, in the pom.xml
file, the dependency declaration tells Maven to download and include the json
library, which is part of the org.json group, and specifically version 20230618, in the project. The build tool Maven will automatically fetch this library from a central repository and make it available for our project to use.
Step 3: Create the Java API controller
This is the part where we add our own Java code to the project and introduce a new API. This new API endpoint defines a POST
HTTP request to the /api/todos
URI served by the Java application.
Create a New Controller Class:
In your project, navigate to
src/main/java/<your-group-id>/<your-artifact-id>/
and create a new directory calledcontroller/
. If you followed the Spring Initializr default prompts then this path would besrc/main/java/com/example/demo/controller/DemoApplication.java
.Create a new Java class file named
TodoController.java
.
Add the Controller Code:
Annotate the class with
@RestController
.Create a method to handle the POST request.
Following is a full example of the TodoController.java
class file:
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;
}
}
Step 4: Run the server
Let’s run the Java Spring Boot API server. We can do that in one of two primary ways:
Use the IDE to run the application, usually by pressing the F5 key.
Navigate to the root directory of the project in the terminal and run the command
mvn spring-boot:run
. This method requires that you have Maven installed in your development environment.
Now we’re ready to test the Java API endpoint and send a POST
request. Use a tool like Postman or cURL to send a POST request to http://localhost:8080/api/todos
. The body should be a JSON string, for example: {"todos": ["Task 1", "Task 2"]}
.
Here is an example of the cURL command that you can use to send the POST request:
sh
curl -X POST http://localhost:8080/api/todos \
-H "Content-Type: application/json" \
-d '{"todos": ["Task 1", "Task 2", "Task 3"]}'
The HTTP response back will then be:
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: 3
Test the Java Spring Boot project with Snyk
In the world of Java development, ensuring the security and integrity of your applications is as crucial as their functionality. One effective tool for this purpose is Snyk, a security platform that specializes in identifying and fixing vulnerabilities in project dependencies.
Java developers often prefer methods more aligned with their existing workflows. Let's explore how to test a Java application using Snyk as a Maven plugin.
Integrate Snyk with Your Maven Project
Snyk provides a Maven plugin that you can add to your pom.xml
. This plugin allows you to run Snyk tests as part of your Maven build process.
Add the Snyk Maven plugin to your pom.xml
:
xml
<plugin>
<groupId>io.snyk</groupId>
<artifactId>snyk-maven-plugin</artifactId>
<version>2.2.0</version>
</plugin>
After setting up the plugin, you'll need to authenticate it with your Snyk account. Sign Up for Snyk if you haven't already, it’s free to create an account on Snyk's website.
Then you can generate a Snyk API token from your Snyk account settings and set it as an environment variable SNYK_TOKEN
on your machine or CI/CD environment. To make it available in the CLI for running the Maven Snyk test we can run:
sh
export SNYK_TOKEN=<the Snyk API token goes here>
You may also choose to provide explicit configuration to the Snyk plugin and set the apiToken
and command-line arguments in a more specific form. This Snyk Maven plugin setting is added to the <plugin>
declaration:
xml
<configuration>
<apiToken>${env.SNYK_TOKEN}</apiToken>
<args>
<arg>--all-projects</arg>
</args>
</configuration>
We can then run the Snyk test:
sh
mvn snyk:test
Oh my, it looks like Snyk found that the new JSONObject dependency we added has some vulnerabilities. Look at the following Maven Snyk test log from the build process:
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, a Java Champion and a security advocate for Snyk wrote up a detailed blog post on using Snyk with the Maven plugin.
Demonstrate the Out of Memory bug in JSONObject
Snyk found a type of Denial of Service vulnerability in the JSON parser package and has provided more information to look up this security issue in Snyk’s vulnerability database.
There, it shows a proof-of-concept that we can apply to understand why our Java API server is vulnerable. Let’s apply the vulnerable input to the JSON parser to see what happens.
Back to our TodoController.java
code, add the following private method which generates a malformed JSON payload:
java
private static String makeNested(int depth) {
if (depth == 0) {
return "{\"a\":1}";
}
return "{\"a\":1;\t\0" + makeNested(depth - 1) + ":1}";
}
Next, update the handleTodo()
API route method to use that string as the input to the JSONObject
constructor:
java
String vulnerablePayload = makeNested(30);
JSONObject jsonData = new JSONObject(vulnerablePayload);
Re-run the Java API server with mvn spring-boot:run
and send the cURL POST HTTP request again and watch how the server pauses for a while until a response is returned. During this time that it pauses, a memory leak occurs and more memory is occupied by the JVM until the Java application eventually crashes.
Snyk already told us how to fix the security issue. If you run the Maven snyk:test
build once again you’ll see that Snyk mentioned how to fix this Denial of Service security vulnerability by upgrading from version 20230618 to version 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@20230618
As such, you should edit the pom.xml
file to update the org.json
dependency version to 20231013
to fix the security issue.
Follow-up resources to write build and ship secure Java applications
You can refer to the full source code used to build the Java Spring Boot API with the JSONObject dependency in the following repository: https://github.com/snyk-snippets/spring-boot-java-jsonobject-vulnerability
If you hold your Java code to high standards and you’re committed to shipping high quality products I’d suggest reviewing the following resources to learn more about the security aspects of Java applications:
10 Java security best practices cheat sheet by Brian Vermeer and Jim Manico
Best practices for managing Java dependencies article by Brian Vermeer
Deep dive into Serialization and deserialization in Java: explaining the Java deserialize vulnerability
Getting started with Snyk for secure Java development.
Snyk’s Maven plugin is open source and available here: https://github.com/snyk/snyk-mvn-plugin
Visit Snyk Learn to learn about Java application security topics
Explore the state of open source security
Understand current trends and approaches to open source software and supply chain security.