In this article
Sécuriser une API Java Spring Boot contre la vulnérabilité de sérialisation JSONObject CVE-2023-5072
La découverte récente de CVE-2023-5072, une vulnérabilité critique liée à la sérialisation JSONObject, a mis en lumière d’importants problèmes de sécurité pour les développeurs Java, en particulier ceux qui utilisent le framework Spring Boot.
Cette vulnérabilité de type épuisement de la mémoire, due à une faille dans la façon dont JSONObject gère la sérialisation, peut exposer les applications Java à diverses menaces de sécurité.
CVE-2023-5072 représente un scénario délicat dans lequel l’intégrité des structures de données peut être compromise lors des processus de sérialisation et de désérialisation. C’est particulièrement préoccupant pour les applications développées avec Spring Boot, largement utilisé pour son efficacité et sa simplicité lors de la création d’applications Java robustes.
Comprendre la nature et les conséquences de telles vulnérabilités est essentiel pour préserver la sécurité et la fiabilité des applications Java dans un environnement numérique de plus en plus complexe.
Dans cet article, nous présentons clairement le problème en créant une API Java simple et proposons des mesures concrètes pour réduire les risques dans un environnement d’API Spring Boot.
Une API Java Spring Boot vulnérable à CVE-2023-5072 dans JSONObject
Pour illustrer la vulnérabilité de sérialisation JSON dans JSONObject, nous allons créer un petit serveur API Java avec Spring Boot et Maven, qui traitera une requête POST à l’adresse /api/todos, analysera un objet JSON contenant un tableau de tâches à faire et renverra leur nombre dans la réponse HTTP.
Commençons par suivre les instructions détaillées pour créer ce projet.
Étape 1 : configurer le projet
1. Créer un projet Spring Boot :
a. Vous pouvez utiliser Spring Initializr pour générer votre projet.
b. Choisissez Maven comme outil de build.
c. Ajoutez « Spring Web » en tant que dépendance.
d. Téléchargez et décompressez le projet.

2. Ouvrir le projet :
a. Ouvrez le projet dans l’IDE de votre choix (par exemple IntelliJ IDEA, Eclipse ou VSCode).
b. Vérifiez que le fichier pom.xml est bien présent, avec la dépendance Spring Boot et une entrée de build pour spring-boot-maven-plugin.
Étape 2 : ajouter les dépendances Java
Comment gérer les dépendances dans pom.xml ?
Dans un fichier pom.xml utilisé par Maven, un outil de build Java populaire, les dépendances (bibliothèques ou modules externes dont votre projet a besoin) sont définies à l’aide d’une combinaison de groupId, artifactId et version. Ensemble, ces éléments identifient de manière unique une version donnée d’une bibliothèque dans les dépôts Maven.
Modifiez le fichier pom.xml et vérifiez que la dépendance Spring Web est présente. Ajoutez la dépendance à la bibliothèque JSONObject. Si elle n’est pas incluse dans Spring Boot, vous pouvez utiliser la bibliothèque JSON de org.json en ajoutant cette dépendance :
xml
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20230618</version>
</dependency>
Examinons la signification de chaque élément, en nous concentrant sur artifactId :
groupId: il s’agit généralement du nom de domaine inversé de l’organisation ou du groupe à l’origine de la bibliothèque. Dans notre exemple d’application,org.jsonest le groupId, ce qui indique que la bibliothèque est maintenue par l’organisation json.org.
artifactId: il s’agit du nom de la bibliothèque ou du module lui-même. C’est le nom que vous utilisez généralement pour désigner une bibliothèque. Ici, l’artifactId estjson: le nom de la bibliothèque que nous ajoutons est donc simplement « json ». Il est courant que l’artifactId reflète la fonctionnalité principale ou l’identité de la bibliothèque.
version: cet élément indique la version précise de la bibliothèque que vous souhaitez utiliser. Dans notre exemple, la version 20230618 est un identifiant unique de la bibliothèque json. La gestion des versions est essentielle pour garantir des builds cohérents, car les versions plus récentes des bibliothèques peuvent introduire des changements incompatibles avec votre projet.
Dans le fichier pom.xml, la déclaration de dépendance indique donc à Maven de télécharger et d’inclure dans le projet la bibliothèque json, qui fait partie du groupe org.json, dans sa version 20230618. L’outil de build Maven récupère automatiquement cette bibliothèque dans un dépôt central et la rend disponible pour notre projet.
Étape 3 : créer le contrôleur de l’API Java
C’est ici que nous ajoutons notre propre code Java au projet et créons une nouvelle API. Ce nouveau point de terminaison d’API définit une requête HTTP POST vers l’URI /api/todos desservie par l’application Java.
Créer une nouvelle classe de contrôleur :
Dans votre projet, accédez à
src/main/java/<your-group-id>/<your-artifact-id>/et créez un nouveau répertoire nommécontroller/. Si vous avez conservé les paramètres par défaut de Spring Initializr, le chemin serasrc/main/java/com/example/demo/controller/DemoApplication.java.Créez un fichier de classe Java nommé
TodoController.java.
Ajouter le code du contrôleur :
Annotez la classe avec
@RestController.Créez une méthode pour traiter la requête POST.
Voici un exemple complet du fichier 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;
}
}Étape 4 : lancer le serveur
Lançons le serveur d’API Java Spring Boot. Deux méthodes principales s’offrent à nous :
Lancez l’application depuis l’IDE, généralement en appuyant sur la touche F5.
Dans le terminal, accédez au répertoire racine du projet et exécutez la commande
mvn spring-boot:run. Cette méthode nécessite que Maven soit installé dans votre environnement de développement.
Nous sommes maintenant prêts à tester le point de terminaison de l’API Java en envoyant une requête POST. Utilisez un outil comme Postman ou cURL pour envoyer une requête POST à http://localhost:8080/api/todos. Le corps doit être une chaîne JSON, par exemple : {"todos": ["Task 1", "Task 2"]}.
Voici un exemple de commande cURL à utiliser pour envoyer la requête POST :
sh
curl -X POST http://localhost:8080/api/todos \
-H "Content-Type: application/json" \
-d '{"todos": ["Task 1", "Task 2", "Task 3"]}'La réponse HTTP renvoyée sera alors la suivante :
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: 3Tester le projet Java Spring Boot avec Snyk
Dans le développement Java, garantir la sécurité et l’intégrité de vos applications est aussi essentiel que d’en assurer le bon fonctionnement. Snyk est un outil efficace à cette fin : cette plateforme de sécurité est spécialisée dans la détection et la correction des vulnérabilités dans les dépendances des projets.
Les développeurs Java privilégient souvent des méthodes qui s’intègrent à leurs workflows existants. Voyons comment tester une application Java à l’aide de Snyk sous forme de plugin Maven.
Intégrer Snyk à votre projet Maven
Snyk fournit un plugin Maven que vous pouvez ajouter à votre fichier pom.xml. Ce plugin vous permet d’exécuter des tests Snyk dans le cadre du processus de build Maven.
Ajoutez le plugin Maven de Snyk à votre fichier pom.xml :
xml
<plugin>
<groupId>io.snyk</groupId>
<artifactId>snyk-maven-plugin</artifactId>
<version>2.2.0</version>
</plugin>Une fois le plugin configuré, vous devez l’authentifier avec votre compte Snyk. Inscrivez-vous à Snyk si ce n’est pas déjà fait : la création d’un compte sur le site de Snyk est gratuite.
Vous pouvez ensuite générer un jeton d’API Snyk dans les paramètres de votre compte et le définir comme variable d’environnement SNYK_TOKEN sur votre machine ou dans votre environnement CI/CD. Pour le rendre accessible dans l’interface CLI afin d’exécuter le test Maven de Snyk, lancez la commande suivante :
sh
export SNYK_TOKEN=<the Snyk API token goes here>Vous pouvez également configurer explicitement le plugin Snyk et définir apiToken ainsi que les arguments de ligne de commande de manière plus précise. Ce paramètre du plugin Maven de Snyk s’ajoute à la déclaration <plugin> :
xml
<configuration>
<apiToken>${env.SNYK_TOKEN}</apiToken>
<args>
<arg>--all-projects</arg>
</args>
</configuration>Nous pouvons ensuite exécuter le test Snyk :
sh
mvn snyk:testEh bien, il semble que Snyk ait détecté des vulnérabilités dans la nouvelle dépendance JSONObject que nous avons ajoutée. Voici le journal du test Maven de Snyk pendant le processus 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 et défenseur de la sécurité chez Snyk, a rédigé un article détaillé sur l’utilisation de Snyk avec le plugin Maven.
Démontrer le problème d’épuisement de la mémoire dans JSONObject
Snyk a détecté un type de vulnérabilité par déni de service dans le package d’analyse JSON et fournit plus d’informations pour examiner ce problème de sécurité dans la base de données des vulnérabilités de Snyk.
Vous y trouverez une preuve de concept que nous pouvons utiliser pour comprendre pourquoi notre serveur d’API Java est vulnérable. Appliquons l’entrée vulnérable à l’analyseur JSON pour voir ce qui se passe.
Revenons au code TodoController.java et ajoutons la méthode privée suivante, qui génère une charge utile JSON malformée :
java
private static String makeNested(int depth) {
if (depth == 0) {
return "{\"a\":1}";
}
return "{\"a\":1;\t\0" + makeNested(depth - 1) + ":1}";
}Ensuite, modifiez la méthode de routage d’API handleTodo() pour utiliser cette chaîne comme entrée du constructeur JSONObject :
java
String vulnerablePayload = makeNested(30);
JSONObject jsonData = new JSONObject(vulnerablePayload);Relancez le serveur d’API Java avec mvn spring-boot:run, puis envoyez à nouveau la requête HTTP POST avec cURL et observez la pause du serveur avant le retour d’une réponse. Pendant ce temps, une fuite de mémoire se produit et la JVM consomme de plus en plus de mémoire, jusqu’à ce que l’application Java finisse par planter.
Snyk nous a déjà indiqué comment corriger le problème de sécurité. Si vous exécutez à nouveau le build Maven snyk:test, vous verrez que Snyk recommande de corriger cette vulnérabilité de sécurité par déni de service en passant de la version 20230618 à la 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@20230618Vous devez donc modifier le fichier pom.xml et mettre à jour la version de la dépendance org.json vers 20231013 afin de corriger le problème de sécurité.
Ressources complémentaires pour écrire, développer et livrer des applications Java sécurisées
Vous trouverez le code source complet utilisé pour créer l’API Java Spring Boot avec la dépendance JSONObject dans le dépôt suivant : https://github.com/snyk-snippets/spring-boot-java-jsonobject-vulnerability
Si vous appliquez des normes élevées à votre code Java et souhaitez livrer des produits de qualité, nous vous recommandons de consulter les ressources suivantes pour en savoir plus sur la sécurité des applications Java :
Aide-mémoire 10 bonnes pratiques de sécurité Java de Brian Vermeer et Jim Manico
Article de Brian Vermeer sur les bonnes pratiques pour gérer les dépendances Java
Analyse approfondie de la sérialisation et de la désérialisation en Java : explication de la vulnérabilité de désérialisation Java
Premiers pas avec Snyk pour le développement Java sécurisé.
Le plugin Maven de Snyk est open source et disponible ici : https://github.com/snyk/snyk-mvn-plugin
Consultez Snyk Learn pour découvrir des sujets liés à la sécurité des applications Java
Découvrez l’état de la sécurité des logiciels open source
Découvrez les tendances et les approches actuelles en matière de logiciels open source et de sécurité de la chaîne d’approvisionnement.