Le risque d’utiliser des objets String en Java / Blogs / Perficient
Si vous êtes un programmeur Java, vous avez peut-être été victime d’une pratique non sécurisée sans le savoir. Nous savons tous (ou devrions savoir) qu’il n’est pas sûr de stocker des mots de passe non cryptés dans la base de données, car cela pourrait compromettre la protection des données au repos. Mais ce n’est pas le seul problème : si à tout moment dans notre code il y a un mot de passe non crypté ou des données sensibles stockées dans une variable String même si elles sont temporaires, alors il pourrait y avoir un risque.
Pourquoi y a-t-il un risque ?
Les objets String n’ont pas été créés pour stocker des mots de passe, ils ont été conçus pour optimiser l’espace dans notre programme. Les objets String en Java sont « immuables », ce qui signifie qu’après avoir créé un objet String et lui avoir attribué une valeur, vous ne pouvez plus supprimer la valeur ni la modifier. Je sais que vous pensez peut-être que ce n’est pas vrai car vous pouvez attribuer « Hello World » à un objet String donné et, dans la ligne suivante, lui attribuer « Au revoir, monde cruel », et c’est techniquement correct. Le problème est que le « Hello World » que vous avez créé en premier va continuer à vivre dans le pool String même si vous ne pouvez pas le voir.
Qu’est-ce que le pool de chaînes ?
Java utilise une zone de mémoire spéciale appelée pool de chaînes pour stocker les littéraux de chaîne. Lorsque vous créez un littéral String, Java vérifie d’abord le pool de chaînes pour voir si une chaîne identique existe déjà. Si tel est le cas, Java réutilisera la référence à la chaîne existante, économisant ainsi de la mémoire. Cela signifie que si vous créez 25 000 objets String et qu’ils ont tous la valeur « Michael Jackson », un seul String Literal sera stocké en mémoire et toutes les variables pointeront vers le même, optimisant ainsi l’espace en mémoire.
Ok, l’objet est dans le pool String, où est le risque ?
L’objet String restera en mémoire pendant un certain temps avant d’être supprimé par le garbage collector. Si un attaquant a accès au contenu de la mémoire, il pourrait obtenir le mot de passe qui y est stocké.
Voyons un exemple basique de ceci. Le code suivant crée un objet String et lui attribue un mot de passe secret : « ¿Ceci est un mot de passe secret ». Ensuite, ce même objet est écrasé 3 fois, et l’inspecteur d’instances du débogueur nous aidera à localiser les objets String commençant par le caractère « ¿ ».
Exemple 1 Code :
Exemple 1 Débogueur :
Comme vous pouvez le remarquer sur l’image lorsque le débogueur est arrivé à la ligne 8, même après avoir changé trois fois la valeur de la variable String « a » et l’avoir mise à null à la fin, toutes les valeurs précédentes restent en mémoire, y compris notre : « ¿Ceci est un mot de passe secret ».
J’ai compris. Le simple fait d’éviter de créer des variables String résoudra le problème, n’est-ce pas ?
Ce n’est pas si simple. Considérons un deuxième exemple. Maintenant, nous sommes plus intelligents et nous allons utiliser un tableau de caractères pour stocker le mot de passe au lieu de la chaîne afin d’éviter le problème de son enregistrement dans le pool de chaînes. De plus, plutôt que d’avoir le mot de passe secret comme littéral dans le code, il sera disponible en clair dans un fichier texte, ce qui n’est d’ailleurs pas recommandé de le sauvegarder en clair, mais nous le ferons pour cet exemple. Un BufferedReader va prendre en charge la lecture du contenu du fichier.
Malheureusement, comme vous le verrez, les mots de passe existent également dans le pool String.
Exemple 2 Code :
Exemple 2 Débogueur :
Ce cas est encore plus déroutant car dans le code aucun objet chaîne n’a jamais été créé, du moins explicitement. Le problème est que BufferedReader.readLine() renvoie temporairement un objet chaîne et que le contenu avec le mot de passe non chiffré restera dans le pool de chaînes.
Que puis-je faire pour résoudre ce problème ?
Dans ce dernier exemple, nous aurons le mot de passe non crypté stocké dans un fichier texte, nous utiliserons un BufferedReader pour lire le contenu du fichier, mais au lieu d’utiliser la méthode BufferedReader.readLine() qui renvoie une chaîne, nous utilisons la méthode BufferedReader. .read() qui stocke le contenu du fichier dans un tableau de caractères. Comme le montre la capture d’écran du débogueur, cette fois, le contenu du fichier n’est pas disponible dans le pool de chaînes.
Exemple 3 Code :
Exemple 3 Débogueur :
En résumé
Pour résoudre ce problème, pensez à suivre les principes énumérés ci-dessous :
- Ne créez pas de littéraux de chaîne contenant des informations confidentielles dans votre code.
- Ne stockez pas d’informations confidentielles dans des objets String. Vous pouvez utiliser d’autres types d’objets pour stocker ces informations, comme le tableau de caractères classique. Après avoir traité les données, assurez-vous d’écraser le tableau de caractères avec des zéros ou des caractères aléatoires, juste pour confondre les attaquants.
- Évitez d’appeler des méthodes qui renverront les informations confidentielles sous forme de chaîne, même si vous ne les enregistrerez pas dans une variable.
- Pensez à appliquer une couche de sécurité supplémentaire en chiffrant les informations confidentielles. Le SealedObject en Java est une excellente alternative pour y parvenir. Le SealedObject est un objet Java dans lequel vous pouvez stocker des données sensibles, vous fournissez une clé secrète et l’objet est crypté et sérialisé. Ceci est utile si vous souhaitez le transmettre et garantir que le contenu ne reste pas exposé. Ensuite, vous pourrez le déchiffrer en utilisant la même clé secrète. Juste un conseil, après l’avoir déchiffré, merci de ne pas le stocker sur un objet String.
Source link