Prévenir la désérialisation non sécurisée dans Node.js
17 avril 2023
0 minutes de lectureNote de la rédaction : 31 mai 2023
Cet article a été mis à jour pour montrer comment Snyk Open Source et l’extension Snyk pour VS Code peuvent vous aider à prévenir la désérialisation non sécurisée dans vos projets.
La sérialisation consiste à convertir un objet JavaScript en un flux d’octets séquentiels afin de l’envoyer sur un réseau ou de l’enregistrer dans une base de données. Elle modifie le format des données d’origine tout en préservant leur état et leurs propriétés, ce qui permet de les recréer au besoin. Grâce à la sérialisation, nous pouvons écrire des données complexes dans des fichiers, des bases de données et la mémoire interprocessus, ainsi que les transmettre entre des composants et sur des réseaux.
La désérialisation est l’inverse de la sérialisation : elle reconvertit les données sérialisées en un objet dans le même état qu’à l’origine. Le problème, c’est que la désérialisation peut être non sécurisée et exposer des données sensibles. La principale cause de la désérialisation non sécurisée est l’absence de protection lors de la désérialisation des entrées utilisateur. Pour l’éviter, il faut veiller à ne désérialiser que des données validées et assainies.
Les attaquants peuvent exploiter cette vulnérabilité en intégrant du code malveillant à un objet sérialisé qui semble inoffensif, puis en l’envoyant à une application. En l’absence de vérifications de sécurité supplémentaires, l’application web désérialise l’objet reçu et exécute le code malveillant. Un attaquant peut également extraire les données sensibles contenues dans un objet sérialisé.
Les conséquences d’une sérialisation non sécurisée peuvent être graves. Lorsqu’un site web désérialise des données de manière non sécurisée, par exemple, les pirates peuvent manipuler des objets sérialisés et transmettre des données nuisibles au code d’une application. Un attaquant peut même envoyer un objet sérialisé entièrement différent pour accéder à d’autres parties de l’application, la rendant vulnérable à d’autres attaques et à la perte de données.
La désérialisation non sécurisée dans Node.js
node-serialize et serialize-to-js sont des packages Node.js et JavaScript vulnérables à la désérialisation non sécurisée. Contrairement à JSON.parse et JSON.stringify, qui ne sérialisent que des objets au format JSON, ces deux bibliothèques sérialisent presque tous les types d’objets, y compris les fonctions. Cette caractéristique les rend vulnérables à la pollution de prototype, une attaque par injection au cours de laquelle un acteur malveillant prend le contrôle des valeurs par défaut des propriétés d’un objet.
En cas de réussite d’une attaque par pollution de prototype, l’attaquant peut causer d’autres dégâts en modifiant la logique de l’application, en déclenchant l’exécution de code à distance ou une attaque par déni de service (DoS).
Cet article montre comment corriger les vulnérabilités de désérialisation dans Node.js. Nous allons créer du code vulnérable, présenter une attaque, puis corriger les vulnérabilités.
Prérequis
Pour suivre ce tutoriel, vous aurez besoin des éléments suivants :
Node.js installé
Connaissances en JavaScript
Corriger les vulnérabilités de désérialisation dans Node.js
Commençons par configurer un projet Node.js. Exécutez la commande suivante :
Installez le framework d’application web Express :
Installez le package de sérialisation Node.js :
Installez un analyseur de cookies :
Créez un nouveau fichier server.js et ajoutez le code ci-dessous :
Dans le fichier ci-dessus, nous utilisons le module non sécurisé node-serialize. Nous lui transmettons également des données non fiables à l’aide de sa fonction unserialize().
Combien de problèmes de sécurité pouvez-vous relever dans le code ci-dessus ? Comment savoir si le code ou les dépendances ajoutées au projet présentent des problèmes de sécurité ?
C’est là qu’intervient l’extension Snyk Code. Elle est gratuite et rapide à configurer. Dans VS Code, accédez à Extensions (SHIFT+CMD+X), recherchez Snyk, installez l’extension et rejoignez plus de 90 000 développeurs.
Voici une capture d’écran de Snyk Code en action avec l’extrait de code de l’application Express précédente :

Observez les flèches violettes et la façon dont elles signalent les problèmes de sécurité potentiels dans ce serveur d’application Express. En examinant attentivement, vous en repérerez d’autres et remarquerez que Snyk Code annote les lignes du fichier `server.js` pour montrer comment du code non sécurisé circule entre différents chemins d’exécution et fichiers de code.
Téléchargez l’extension Snyk pour VS Code, elle est gratuite !
Revenons à l’application web Node.js : le résultat, c’est que le point de terminaison app.get('/', function(req, res) devient vulnérable à la désérialisation non sécurisée. En effet, l’application ajoute les entrées utilisateur à la valeur d’un cookie de préférences déjà sérialisé.
Pour démontrer la vulnérabilité, exécutez le serveur Express à l’aide de cette commande :
Le serveur démarre sur le port 3000. Ouvrez http://localhost:3000/ pour afficher la page et inspecter les cookies, comme indiqué ci-dessous :

Convertissez maintenant le cookie du format base64 au format JSON. Vous pouvez utiliser un outil en ligne comme Code Beautify. Voici le cookie en base64 :
Et voici le cookie défini par le serveur après sa conversion de base64 en JSON :
Pour voir la désérialisation problématique en action, modifions la valeur de l’objet JSON et encodons-le en base64, puis remplaçons la valeur du cookie dans le navigateur par le nouveau cookie.
Modifiez l’objet JSON comme suit :
Ouvrez cet outil Base64 pour encoder le JSON. Une fois encodé en base64, il donne :
Remplacez maintenant la valeur actuelle du cookie dans le navigateur par la valeur encodée du nouvel objet, puis modifiez la valeur de la réponse HTTP dans le fichier server.js comme suit :
Lorsque vous redémarrez la connexion et actualisez la page, vous verrez la nouvelle réponse.
Exploiter la vulnérabilité
Exploitons la vulnérabilité pour exécuter du code arbitraire (ACE), en transmettant des données non fiables à la fonction unserialize().
Commençons par créer une charge utile avec la fonction serialize() du même module que celui utilisé jusqu’ici. Pour cela, créez un fichier nommé serialize.js et ajoutez-y le code suivant :
Ce script Node.js va sérialiser notre code.
Ouvrez maintenant un terminal, accédez au dossier racine et exécutez cette commande :
Remplacez le paramètre username dans la valeur du cookie par la valeur du paramètre myOutput dans le résultat ci-dessus. Notre nouvel objet dans le cookie devrait maintenant ressembler à ceci :
Le résultat ci-dessus nous fournit une charge utile sérialisée à transmettre à la fonction unserialize(). Pour obtenir une ACE, nous devons utiliser une expression de fonction immédiatement invoquée (IIFE) JavaScript afin d’appeler la fonction.
Lorsque nous ajoutons les parenthèses IIFE, (), après le corps de la fonction dans cette charge utile sérialisée, la fonction s’exécute après la création de l’objet. Voici l’objet JSON qui sera alors stocké dans le cookie.
Pour montrer comment cet objet manipulé introduit l’évaluation dynamique de code dans un serveur Node.js en cours d’exécution, nous allons l’utiliser dans notre fichier utilitaire serialize.js. Transmettons cette charge utile à une fonction unserialize(). Modifiez le fichier seralize.js en ajoutant le code suivant :
Dans le code ci-dessus, nous avons ajouté une nouvelle variable, km, qui contient une fonction comme valeur de username, transmise à la fonction unserialize().
Exécutez maintenant le script utilitaire à l’aide de la commande suivante pour vérifier si la fonction est exécutée :
Vous devriez obtenir le résultat suivant :

L’exploitation du processus de sérialisation a réussi ! En tant qu’attaquants capables de contrôler l’entrée de l’objet côté client, nous avons pu le manipuler pour y inclure du code JavaScript qui modifie le champ du nom d’utilisateur.
Corriger la vulnérabilité de désérialisation non sécurisée
Le meilleur moyen de prévenir la désérialisation non sécurisée est d’éviter complètement de désérialiser les entrées utilisateur. La deuxième meilleure solution consiste à vérifier les entrées utilisateur avant de les sérialiser. Nous pouvons utiliser un package appelé Serialize JavaScript pour assainir les entrées dans l’exemple vulnérable ci-dessus.
Commencez par installer serialize-javaScript avec npm :
Modifiez le fichier serialize.js en remplaçant son code par celui-ci :
ignoreFunction garantit que les fonctions utilisées pour exécuter une ACE ne sont pas sérialisées. Exécutez le fichier pour le vérifier :
Voici le résultat :

Vérifions que le nouveau package serialize-javascript recommandé dans cet article est bien exempt de vulnérabilités, selon les rapports CVE. La commande npm ls ci-dessous montre qu’il a été ajouté au projet, aux côtés du package vulnérable node-serialize :
Analysons-le ensuite avec le package npm gratuit snyk :
Cela montre que le package npm node-serialize est vulnérable, comme nous l’avons démontré dans cet article, et qu’aucune vulnérabilité de sécurité publiquement connue n’affecte le package npm serialize-javascript recommandé.
Nous vous recommandons vivement de commencer à utiliser Snyk Open Source et d’intégrer le package npm snyk à votre workflow de développement habituel. Pensez à l’ajouter à votre pipeline CI/CD pour éviter l’introduction de nouvelles vulnérabilités de sécurité.
Bonnes pratiques pour éviter les vulnérabilités de désérialisation
La désérialisation des entrées utilisateur peut permettre à des acteurs malveillants d’attaquer un système et d’exposer des données sensibles. Le meilleur moyen de se protéger contre la désérialisation non sécurisée est d’éviter de désérialiser des données provenant de sources non fiables. Si nous devons désérialiser des données, nous devrions mettre en place des mesures de sécurité supplémentaires pour vérifier qu’elles n’ont pas été manipulées.
Nous devrions également assainir les entrées. Pour cela, nous pouvons vérifier si l’objet à sérialiser contient des fonctions et interrompre la sérialisation si c’est le cas.
Désérialisation sécurisée
La sérialisation et la désérialisation sont des processus essentiels qui permettent de transférer facilement des données sur un réseau. Cependant, une désérialisation non sécurisée peut entraîner des vulnérabilités critiques. En injectant des objets sérialisés malveillants dans une application web, généralement sous la forme de données fournies par l’utilisateur puis désérialisées, les attaquants peuvent transmettre des données nuisibles à une application. Ils peuvent ensuite lancer une attaque par injection, exécuter du code à distance ou déclencher une attaque DDoS.
Nous pouvons prévenir les attaques liées à cette vulnérabilité en évitant simplement de désérialiser les entrées utilisateur. Si la désérialisation est nécessaire, nous devrions ajouter des mécanismes de sécurité, tels que des jetons anti-contrefaçon, pour garantir que les données n’ont pas été modifiées.
Consultez le blog de Snyk pour en savoir plus sur la prévention des vulnérabilités de sérialisation et de désérialisation non sécurisées en Java.
Pour protéger votre code contre les vulnérabilités de sérialisation et de désérialisation non sécurisées, ajoutez le plugin Snyk.io à votre IDE.
Lancez-vous dans les compétitions Capture The Flag
Apprenez à résoudre des défis Capture The Flag en regardant à la demande notre atelier virtuel d’initiation.
