43 votes

Comment s'assurer de la destruction d'un objet de type String en Java?

Un empoyee à mon entreprise a besoin de modifier les données d'une base de données SQL Server par le biais d'un programme que j'ai fait. Le programme utilise l'authentification Windows en premier, et j'ai demandé aux Administrateurs de bases de données pour donner à cet utilisateur l'accès en écriture à ladite base de données.

Ils n'étaient pas prêts à le faire, et ont plutôt donné l'accès en écriture à mon compte d'utilisateur Windows.

Depuis que j'ai confiance en lui, mais pas assez pour lui laisser le travail de 90 minutes avec ma session est ouverte, je vais juste ajouter une invite de connexion à mon programme, demandant un nom d'utilisateur et le mot de passe et connectez-vous à SQL Server avec elle. Je vais ouvrir une session, et la confiance de ma demande de le laisser faire ce qu'il doit.

Cela pose toutefois un petit risque pour la sécurité. Les champs de mot de passe tutoriel sur Sund'Oracle site précise que les mots de passe doivent être conservées que le temps minimum requis dans la mémoire, et, à cette fin, l' getPassword méthode retourne un char[] tableau que vous pouvez zéro une fois que vous avez terminé avec elle.

Cependant, Java DriverManager classe n'accepte String objets comme des mots de passe, donc je ne vais pas être en mesure de disposer du mot de passe dès que j'ai terminé avec elle. Et depuis mon application est d'ailleurs assez faible sur les allocations et les exigences de la mémoire, qui sait combien de temps il survivra dans la mémoire? Le programme sera exécuté pendant une période relativement longue, comme indiqué ci-dessus.

Bien sûr, je ne peux pas contrôler ce que le Serveur SQL server JDBC classes de faire avec mon mot de passe, mais j'espère que je pouvais contrôler ce que je fais avec mon mot de passe.

Est-il un moyen infaillible pour détruire/zéro une String objet avec Java? Je sais que les deux sont des sortes de contre la langue (destruction des objets est non-déterministe, et String des objets sont immuables), et System.gc() est du genre imprévisible, mais encore; une idée?

25voto

jtahlborn Points 32515

Donc, voici la mauvaise nouvelle. je suis surpris que personne n'en a parlé-il encore. moderne, ramasseurs d'ordures, même à l'ensemble de char[] concept est cassé. indépendamment de si vous utilisez une Chaîne de caractères ou un char[], les données peuvent finir par vivre dans la mémoire de qui-sait-comment-long. pourquoi est-ce? parce que les machines virtuelles utilisent générationnel éboueurs qui, en somme, la copie des objets de tous sur la place. donc, même si vous utilisez un char[], la mémoire qu'il utilise peut être copiée à divers endroits dans le tas, en laissant copie du mot de passe partout où il va (et pas performant gc va remettre à zéro les vieux de la mémoire). ainsi, lorsque vous zéro sur l'instance que vous avez à la fin, vous n'êtes qu'une réinitialisation de la dernière version en mémoire.

longue histoire courte, il n'y a pas à l'épreuve des balles façon de le gérer. vous devez faire confiance à la personne.

12voto

Mark Peters Points 42201

Je ne peux que penser à une solution à l'aide de la réflexion. Vous pouvez utiliser la réflexion pour appeler le constructeur privé qu'un tableau de caractères:

  char[] chars = {'a', 'b', 'c'};
  Constructor<String> con = String.class.getDeclaredConstructor(int.class, int.class, char[].class);
  con.setAccessible(true);
  String password = con.newInstance(0, chars.length, chars);
  System.out.println(password);

  //erase it
  Arrays.fill(chars, '\0');
  System.out.println(password);

Modifier

Pour toute personne de penser que c'est une failproof ou même utile précaution, je vous encourage à lire jtahlborn réponse pour au moins une mise en garde.

5voto

necromancer Points 4120

si vous devez absolument, garder un WeakReference à la chaîne, et de garder avalant de la mémoire jusqu'à ce que vous la force de collecte des ordures de la chaîne qui permet de détecter en testant si le weakreference est devenue nulle. cela peut encore laisser les octets dans l'espace d'adressage du processus. peut-être un couple de plus en bidons de le garbage collector serait vous donner le confort? ainsi, après votre chaîne d'origine weakreference ai entaché de nullité, en créer un autre, weakreference et le taux de désabonnement jusqu'à ce qu'il est mis à zéro, ce qui impliquerait un plein de collecte des ordures cycle a été fait.

d'une certaine manière, j'ai du ajouter LOL à cela, même si ma réponse ci-dessus est tout à fait sérieux :)

3voto

Cratylus Points 21838

Je ne suis pas sûr de l' DriverManager classe.
En règle générale, vous avez raison, la recommandation est de stocker le mot de passe dans des tableaux de char et explicitement effacer la mémoire après utilisation.
L'exemple le plus courant:

KeyStore store = KeyStore. getInstance(KeyStore, getDefaultType()) ;
char[] password = new char[] {'s','e','c','r','e','t'};
store .load(is, password );
//After finished clear password!!!
Arrays. fill(password, '\u0000' ) ;

Dans la JSSE et JCA la conception avait exactement cela à l'esprit. C'est pourquoi l'Api s'attendre à un char[] , et non une Chaîne de caractères.
Car, comme vous le savez très bien les Chaînes sont immuables, le mot de passe est admissible à l' avenir la collecte des ordures et vous ne pouvez pas réinitialiser par la suite. Cela peut entraîner des risques de sécurité, les programmes malveillants qui snoop zones de mémoire.
Je ne pense pas que dans ce cas, vous êtes à la recherche dans il y a un travail autour de.
En fait il y a une question similaire ici:
Pourquoi Gestionnaire de Pilote de ne pas utiliser des tableaux de char?
mais il n'y a pas de réponse claire.
Il semble que le concept est que le mot de passe est déjà stocké dans un fichier de propriétés (il y a déjà un DriverManager constructeur acceptant propriétés) et donc le fichier lui-même impose déjà à un plus grand risque que le chargement du mot de passe à partir d'un fichier dans une chaîne.
Ou les concepteurs de l'API avait certaines hypothèses sur la sécurité de la machine d'accéder à la DB.
Je pense que la solution la plus sûre serait d'essayer d'y regarder, si c'est possible, sur la façon dont le DriverManager utilise le mot de passe c'est à dire tient-il à une référence interne etc.

2voto

Robert Munteanu Points 31558

Vous pouvez modifier la valeur de l'intérieur de la char[] l'aide de la réflexion.

Vous devez être prudent de changer avec un tableau de la même longueur, ou également de mettre à jour l' count champ. Si vous voulez être en mesure de l'utiliser comme une entrée dans un jeu ou comme une valeur de la carte, vous aurez besoin de recalculer le code de hachage et de définir la valeur de l' hashCode champ.

Cela étant dit, le code minimal à atteindre c'est

        String password = "password";

        Field valueField = String.class.getDeclaredField("value");
        valueField.setAccessible(true);
        char[] chars  = (char[]) valueField.get(password);

        chars[0] = Character.valueOf('h');

        System.out.println(password);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X