web-dev-qa-db-fra.com

Pourquoi est-ce que char [] est préféré à String pour les mots de passe?

Dans Swing, le champ de mot de passe a une méthode getPassword() (renvoie char[]) au lieu de la méthode habituelle getText() (renvoie String). De même, j'ai suggéré de ne pas utiliser String pour gérer les mots de passe.

Pourquoi String constitue-t-il une menace pour la sécurité en ce qui concerne les mots de passe? Il n’est pas pratique d’utiliser char[].

3086
Ahamed

Les chaînes sont immuables. Cela signifie qu'une fois que vous avez créé la variable String, si un autre processus peut vider la mémoire, il n’est pas possible (à part reflet ) de supprimer les données avant garbage collection kicks in.

Avec un tableau, vous pouvez explicitement effacer les données une fois que vous en avez fini. Vous pouvez écraser le tableau avec tout ce que vous voulez et le mot de passe ne sera présent nulle part dans le système, même avant la récupération de place.

Donc oui, ceci est est un problème de sécurité - mais même utiliser char[] ne réduit que la fenêtre d'opportunité pour un attaquant, et ce n'est que pour ce type d'attaque spécifique.

Comme indiqué dans les commentaires, il est possible que les tableaux déplacés par le ramasse-miettes conservent des copies parasites des données en mémoire. Je crois que cela est spécifique à l’implémentation - le ramasse-miettes peut efface toute la mémoire au fur et à mesure, pour éviter ce genre de chose. Même si c'est le cas, il y a toujours le temps pendant lequel le char[] contient les caractères réels en tant que fenêtre d'attaque.

3941
Jon Skeet

Bien que d’autres suggestions semblent valables, il existe une autre bonne raison. Avec plain String, vous avez beaucoup plus de chances de imprimer accidentellement le mot de passe dans des journaux , des moniteurs ou tout autre endroit non sécurisé. char[] est moins vulnérable.

Considère ceci:

public static void main(String[] args) {
    Object pw = "Password";
    System.out.println("String: " + pw);

    pw = "Password".toCharArray();
    System.out.println("Array: " + pw);
}

Impressions:

String: Password
Array: [C@5829428e
1136
Konrad Garus

Pour citer un document officiel, le Guide de l’architecture de la cryptographie Java dit ceci à propos des mots de passe char[] vs. String (à propos du cryptage basé sur le mot de passe, mais plus généralement des mots de passe):

Il semblerait logique de collecter et de stocker le mot de passe dans un objet de type Java.lang.String. Cependant, voici l'avertissement: Objects of Les types String sont immuables, c’est-à-dire qu’aucune méthode n'est définie. vous permettent de modifier (écraser) ou de mettre à zéro le contenu d'une String après utilisation. Cette fonctionnalité rend les objets String inappropriés pour stocker des informations sensibles telles que les mots de passe utilisateur. Vous devrait toujours collecter et stocker des informations sensibles relatives à la sécurité dans un char tableau à la place.

La directive 2-2 des directives de codage sécurisé pour le langage de programmation Java, version 4.0 dit également quelque chose de similaire (bien qu’elle se trouve à l’origine dans le contexte de la journalisation):

Directive 2-2: Ne pas enregistrer les informations très sensibles

Certaines informations, telles que les numéros de sécurité sociale (SSN) et les mots de passe, est très sensible. Ces informations ne doivent pas être conservées plus longtemps que nécessaire ni où il peut être vu, même par administrateurs. Par exemple, il ne devrait pas être envoyé aux fichiers journaux et à sa présence ne devrait pas être détectable par des recherches. Certains transitoires les données peuvent être conservées dans des structures de données modifiables, telles que des tableaux de caractères et effacé immédiatement après utilisation. Effacement des structures de données a réduit efficacité sur les systèmes d’exécution Java typiques à mesure que les objets sont déplacés mémoire de manière transparente au programmeur.

Cette directive a également des implications pour la mise en œuvre et l'utilisation de bibliothèques de niveau inférieur n'ayant pas une connaissance sémantique des données ils traitent avec. Par exemple, une analyse de chaîne de bas niveau La bibliothèque peut enregistrer le texte sur lequel elle fonctionne. Une application peut analyser un SSN avec la bibliothèque. Cela crée une situation où les SSN sont disponible pour les administrateurs ayant accès aux fichiers journaux.

639
Bruno

Les tableaux de caractères (char[]) peuvent être effacés après utilisation en définissant chaque caractère à zéro et sans chaînes. Si quelqu'un peut voir l'image mémoire, il peut voir un mot de passe en texte brut si des chaînes sont utilisées, mais si char[] est utilisé, après la purge des données avec des 0, le mot de passe est sécurisé.

326
alephx

Certaines personnes pensent que vous devez écraser la mémoire utilisée pour stocker le mot de passe dès que vous n'en avez plus besoin. Cela réduit la fenêtre de temps qu'un attaquant doit lire le mot de passe de votre système et ignore complètement le fait qu'il a déjà besoin d'un accès suffisant à Hijack la mémoire de la machine virtuelle Java pour le faire. Un attaquant disposant de beaucoup d'accès peut capturer vos événements clés, ce qui le rend totalement inutile (autant que je sache, alors corrigez-moi si je me trompe).

Mettre à jour

Grâce aux commentaires, je dois mettre à jour ma réponse. Apparemment, il existe deux cas où cela peut ajouter une (très) mineure amélioration de la sécurité, car cela réduit le temps qu'un mot de passe risque d'atteindre sur le disque dur. Néanmoins, je pense que c'est exagéré pour la plupart des cas d'utilisation.

  • Votre système cible est peut-être mal configuré ou vous devez le supposer et vous devez être paranoïaque à propos des vidages mémoire (peut être valable si les systèmes ne sont pas gérés par un administrateur). 
  • Votre logiciel doit être excessivement paranoïaque pour éviter les fuites de données et permettre à l’attaquant d’avoir accès au matériel - en utilisant des éléments tels que TrueCrypt (interrompu), VeraCrypt ou CipherShed .

Si possible, la désactivation des vidages principaux et du fichier d'échange permettrait de résoudre ces deux problèmes. Cependant, ils nécessiteraient des droits d'administrateur et pourraient réduire les fonctionnalités (moins de mémoire à utiliser) et extraire RAM d'un système en cours de fonctionnement resterait une préoccupation valable.

201
josefx
  1. Les chaînes sont immuables en Java, si vous stockez le mot de passe sous forme de texte brut, il sera disponible en mémoire jusqu'à ce que Garbage Collector le supprime et, dans la mesure où les chaînes sont utilisées dans le pool String pour être réutilisées, il y a de fortes chances pour qu'elles restent mémoire pendant une longue durée, ce qui pose un risque pour la sécurité. Toute personne ayant accès au vidage de la mémoire peut trouver le mot de passe en texte clair. 
  2. Java recommendation using getPassword(), méthode de JPasswordField qui renvoie une méthode char [] et obsolète getText(), qui renvoie le mot de passe en texte clair indiquant le motif de la sécurité. 
  3. toString () il y a toujours un risque d’impression de texte brut dans le fichier journal ou la console, mais si vous utilisez Array, vous n'imprimerez pas le contenu du tableau, mais son emplacement mémoire sera imprimé. 

    String strPwd = "passwd";
    char[] charPwd = new char[]{'p','a','s','s','w','d'};
    System.out.println("String password: " + strPwd );
    System.out.println("Character password: " + charPwd );
    

    Mot de passe de la chaîne: passwd

    Mot de passe du personnage: [C @ 110b2345

Remarques finales: Bien que l’utilisation de char [] ne soit pas suffisante, vous devez effacer le contenu pour plus de sécurité. Je suggère également de travailler avec un mot de passe crypté ou haché à la place du texte brut et de l'effacer de la mémoire dès que l'authentification est terminée.

131
Srujan Kumar Gulla

Je ne pense pas que ce soit une suggestion valable, mais, je peux au moins deviner la raison.

Je pense que la motivation est de vouloir s'assurer que vous pouvez effacer toute trace du mot de passe en mémoire rapidement et avec certitude après son utilisation. Avec un char[], vous pouvez écraser chaque élément du tableau avec un blanc ou quelque chose de sûr. Vous ne pouvez pas modifier la valeur interne d'une String de cette façon.

Mais cela seul n'est pas une bonne réponse. pourquoi ne pas simplement s'assurer qu'une référence à char[] ou String n'échappe pas? Ensuite, il n'y a pas de problème de sécurité. Mais le fait est que les objets String peuvent être intern()ed en théorie et conservés en vie dans le pool constant. Je suppose que l'utilisation de char[] interdit cette possibilité.

76
Sean Owen

La réponse a déjà été donnée, mais j'aimerais partager un problème que j'ai découvert récemment avec les bibliothèques standard Java. Alors qu'ils prennent bien soin de remplacer les chaînes de mots de passe par char[] partout (ce qui est bien sûr une bonne chose), d'autres données critiques pour la sécurité semblent être négligées lorsqu'il s'agit de les effacer de la mémoire.

Je pense par exemple à le PrivateKey class. Envisagez un scénario dans lequel vous chargeriez une clé RSA privée à partir d'un fichier PKCS # 12, en l'utilisant pour effectuer certaines opérations. Désormais, dans ce cas, la détection du mot de passe seul ne vous aiderait pas beaucoup tant que l'accès physique au fichier de clé est correctement limité. En tant qu’attaquant, il serait beaucoup plus avantageux d’obtenir la clé directement au lieu du mot de passe. Les informations souhaitées peuvent être des fuites de collecteur, des vidages de mémoire, une session de débogage ou des fichiers d'échange ne sont que quelques exemples.

Et comme il s'avère, rien ne vous permet d'effacer de la mémoire les informations privées d'une PrivateKey, car aucune API ne vous permet d'effacer les octets constituant les informations correspondantes.

C'est une mauvaise situation, car ce papier décrit comment cette situation pourrait être potentiellement exploitée.

La bibliothèque OpenSSL, par exemple, écrase les sections de mémoire critiques avant que les clés privées ne soient libérées. Étant donné que Java est un déchet ordonné, nous aurions besoin de méthodes explicites pour effacer et invalider les informations privées des clés Java, qui doivent être appliquées immédiatement après l'utilisation de la clé. 

61
emboss

Comme le dit Jon Skeet, il n’ya aucun moyen d’utiliser la réflexion. 

Cependant, si la réflexion est une option pour vous, vous pouvez le faire.

public static void main(String[] args) {
    System.out.println("please enter a password");
    // don't actually do this, this is an example only.
    Scanner in = new Scanner(System.in);
    String password = in.nextLine();
    usePassword(password);

    clearString(password);

    System.out.println("password: '" + password + "'");
}

private static void usePassword(String password) {

}

private static void clearString(String password) {
    try {
        Field value = String.class.getDeclaredField("value");
        value.setAccessible(true);
        char[] chars = (char[]) value.get(password);
        Arrays.fill(chars, '*');
    } catch (Exception e) {
        throw new AssertionError(e);
    }
}

quand couru

please enter a password
hello world
password: '***********'

Remarque: si le caractère de la chaîne [] a été copié dans le cadre d'un cycle GC, il se peut que la copie précédente se trouve quelque part en mémoire. 

Cette ancienne copie n'apparaît pas dans un vidage de tas, mais si vous avez un accès direct à la mémoire brute du processus, vous pouvez la voir. En général, vous devriez éviter que quiconque ait un tel accès.

46
Peter Lawrey

Ce sont toutes les raisons, il faut choisir un tableau char [] au lieu de String pour un mot de passe.

1. Étant donné que les chaînes sont immuables en Java, si vous stockez le mot de passe sous forme de texte brut, il sera disponible en mémoire jusqu'à ce que le récupérateur de place le supprime. Depuis que String est utilisé dans le pool String pour être réutilisable, Il y a de fortes chances pour qu'il reste en mémoire longtemps, ce qui pose un risque pour la sécurité. 

Étant donné que toute personne ayant accès au vidage de la mémoire peut trouver le mot de passe en texte clair, vous devez toujours utiliser un mot de passe crypté plutôt que du texte brut. Étant donné que les chaînes sont immuables, il est impossible de modifier le contenu des chaînes car toute modification produira une nouvelle chaîne. Toutefois, si vous utilisez un caractère [], vous pouvez toujours définir tous les éléments comme vides ou nuls. Donc, stocker un mot de passe dans un tableau de caractères atténue clairement le risque de sécurité lié au vol d'un mot de passe.

2. Java lui-même recommande d'utiliser la méthode getPassword () de JPasswordField qui renvoie un caractère char [] au lieu de la méthode obsolète getText () qui renvoie les mots de passe en texte clair pour des raisons de sécurité. Il est bon de suivre les conseils de l'équipe Java et de respecter les normes plutôt que de s'y opposer.

(3. Avec String, il y a toujours un risque d'imprimer du texte brut dans un fichier journal ou une console, mais si vous utilisez un tableau, vous n'imprimerez pas le contenu d'un tableau, mais son emplacement mémoire sera imprimé. Bien que ce ne soit pas une vraie raison, cela reste logique.

String strPassword="Unknown";
char[] charPassword= new char[]{'U','n','k','w','o','n'};
System.out.println("String password: " + strPassword);
System.out.println("Character password: " + charPassword);

String password: Unknown
Character password: [C@110b053

Référencé depuis ce blog . J'espère que cela aide.

37
Human Being

Il n'y a rien que tableau de caractères vous donne vs String sauf si vous le nettoyez manuellement après utilisation, et je n'ai vu personne le faire. Donc, pour moi, la préférence de char [] vs String est un peu exagérée.

Regardez le largement utilisé La bibliothèque Spring Security ici et demandez-vous - les mots de passe Spring Security sont incompétents ou char [] n'ont-ils pas beaucoup de sens? Quand un méchant pirate récupère la mémoire de votre RAM, assurez-vous qu'il obtiendra tous les mots de passe, même si vous utilisez des méthodes sophistiquées pour les masquer.

Cependant, Java change tout le temps et certaines fonctionnalités redoutables, telles que la fonctionnalité de déduplication de chaînes de Java 8 pourraient interner des objets String à votre insu. Mais c'est une conversation différente.

33
Oleg Mikheev

Les chaînes sont immuables et ne peuvent pas être modifiées une fois qu'elles ont été créées. La création d'un mot de passe sous forme de chaîne laissera des références parasites au mot de passe sur le tas ou sur le pool de chaînes. Désormais, si une personne effectue un dump du processus Java et l’analyse minutieusement, il pourra peut-être deviner les mots de passe. Bien sûr, ces chaînes non utilisées seront récupérées, mais cela dépend du moment où le GC entre en action.

De l'autre côté, les caractères [] sont modifiables dès que l'authentification est terminée, vous pouvez les écraser avec n'importe quel caractère comme tous les M ou les barres obliques inverses. Maintenant, même si quelqu'un effectue un vidage de tas, il pourrait ne pas être en mesure d'obtenir les mots de passe qui ne sont pas utilisés actuellement. Cela vous donne plus de contrôle dans le sens où vous effacez vous-même le contenu de l’objet ou attendez que le GC le fasse.

29
Geek

1) Puisque les chaînes sont immuables en Java si vous stockez le mot de passe sous forme de texte brut, il sera disponible en mémoire jusqu'à ce que Garbage Collector le supprime et, puisque String est utilisé dans le pool String pour être réutilisé, il y a de fortes chances qu'il reste en mémoire. pour la longue durée, ce qui pose une menace pour la sécurité. Comme toute personne ayant accès à la mémoire mémoire peut trouver le mot de passe en texte clair, vous devez toujours utiliser un mot de passe crypté au lieu du texte brut. Étant donné que les chaînes sont immuables, il est impossible de modifier le contenu des chaînes car toute modification produira une nouvelle chaîne. Par ailleurs, si vous char [], vous pouvez toujours définir tous ses éléments comme vides ou nuls. Donc, le fait de stocker le mot de passe dans la matrice de caractères atténue le risque de sécurité lié au vol du mot de passe.

2) Java lui-même recommande d'utiliser la méthode getPassword () de JPasswordField, qui renvoie une méthode char [] et getText () dépréciée, qui renvoie le mot de passe en texte clair indiquant le motif de la sécurité. Il est bon de suivre les conseils de l'équipe Java et de respecter les normes plutôt que de s'y opposer.

16
Neeraj Gahlawat

La réponse courte et directe serait parce que char[] est mutable alors que les objets String ne le sont pas.

Strings en Java sont des objets immuables. C'est pourquoi ils ne peuvent pas être modifiés une fois créés. Par conséquent, le seul moyen de supprimer leur contenu de la mémoire consiste à les récupérer. Ce ne sera qu'alors que la mémoire libérée par l'objet pourra être écrasée et que les données disparaîtront.

Maintenant, la récupération de place en Java ne se produit pas à un intervalle garanti. La String peut donc rester longtemps en mémoire et si un processus se bloque pendant cette période, le contenu de la chaîne peut se retrouver dans un vidage de mémoire ou dans un journal. 

Avec un caractère tableau , vous pouvez lire le mot de passe, finir de le manipuler dès que vous le pouvez, puis changer immédiatement le contenu.

16
Pritam Banerjee

String est immuable et va au pool de chaînes. Une fois écrit, il ne peut pas être écrasé.

char[] est un tableau que vous devriez écraser une fois que vous avez utilisé le mot de passe. Voici comment procéder:

char[] passw = request.getPassword().toCharArray()
if (comparePasswords(dbPassword, passw) {
 allowUser = true;
 cleanPassword(passw);
 cleanPassword(dbPassword);
 passw=null;
}

private static void cleanPassword (char[] pass) {
 for (char ch: pass) {
  ch = '0';
 }
}

Un scénario dans lequel l'attaquant pourrait l'utiliser est un crashdump - lorsque la machine virtuelle Java se bloque et génère un dump de mémoire - vous pourrez voir le mot de passe.

Ce n'est pas nécessairement un attaquant externe malveillant. Il peut s'agir d'un utilisateur de support ayant accès au serveur à des fins de surveillance. Il pouvait jeter un coup d'œil dans un crash et trouver les mots de passe.

15
ACV

String en Java est immuable. Ainsi, chaque fois qu'une chaîne est créée, elle restera en mémoire jusqu'à ce qu'elle soit nettoyée. Ainsi, toute personne ayant accès à la mémoire peut lire la valeur de la chaîne. 
Si la valeur de la chaîne est modifiée, une nouvelle chaîne sera créée. Ainsi, la valeur d'origine et la valeur modifiée restent dans la mémoire jusqu'à ce qu'elles soient récupérées. 

Avec le tableau de caractères, le contenu du tableau peut être modifié ou effacé une fois que l'objectif du mot de passe est servi. Le contenu original du tableau ne sera pas trouvé dans la mémoire après sa modification et même avant que la récupération de place ne démarre.

Pour des raisons de sécurité, il est préférable de stocker le mot de passe sous forme de tableau de caractères.

11
Saathvik

L'utilisation d'un mot de passe dans String ou Char [] est toujours discutable, car les deux approches avaient également leur utilité et leur inconvénient. Cela dépend du besoin auquel l'utilisateur s'attend à être satisfait. Les lignes ci-dessous peuvent aider à mieux comprendre quand utiliser quel conteneur: Puisque String en Java est immuable, chaque fois que quelqu'un essaye de manipuler votre chaîne, il crée un nouvel objet et l'existant reste non-tempéré. Cela pourrait être considéré comme un avantage pour stocker le mot de passe en tant que chaîne mais en revanche. L'objet String reste en mémoire même après utilisation. Donc, d’une manière ou d’une autre, si une personne ayant l’emplacement mémoire peut facilement retracer votre mot de passe stocké à cet emplacement… .. Tout en venant à Char [], ce dernier est modifiable, mais il présente l’avantage qu’après l’utilisation, le programmeur peut explicitement nettoyer le tableau ou remplacer les valeurs. Ainsi, après aucune utilisation, il est nettoyé et personne ne peut jamais savoir ce que vous avez stocké.

Selon les circonstances ci-dessus, on peut avoir l’idée de choisir String ou Char [] pour répondre à leurs besoins.

Merci.

1
Neeraj

En un mot, c'est à cause de ce qui peut arriver au mot de passe stocké une fois que vous avez fini de l'utiliser. 

Une chaîne peut rester (potentiellement) en mémoire longtemps après que vous ayez cessé de l’utiliser, mais vous pouvez vider un tableau de caractères afin qu’il ne contienne plus de données.

Tout cela a à voir avec la façon dont les éboueurs et objets immuables fonctionnent.

0
hamza belmellouki