Les cordes sont immuables . Cela signifie qu'une fois que vous avez créé le String
, si un autre processus peut vider la mémoire, il n'y a aucun moyen (à part la réflexion ) que vous puissiez vous débarrasser des données avant que la collecte des ordures n'intervienne.
Avec un tableau, vous pouvez effacer explicitement les données une fois que vous en avez terminé. 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 le ramassage des ordures.
Alors oui, c'est un problème de sécurité - mais même l'utilisation char[]
ne fait que réduire 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 garbage collector laissent des copies parasites des données en mémoire. Je crois que cela est spécifique à l'implémentation - le garbage collector peut effacer toute la mémoire au fur et à mesure, pour éviter ce genre de chose. Même si c'est le cas, il reste le temps pendant lequel le char[]
contient les personnages réels comme fenêtre d'attaque.
char[]
emplacements coupe cette ligne d'attaque, ce qui n'est pas possible lors de l'utilisationString
.Alors que d'autres suggestions semblent valables ici, il y a une autre bonne raison. Avec plain,
String
vous avez beaucoup plus de chances d' imprimer accidentellement le mot de passe dans les journaux , les moniteurs ou tout autre endroit non sécurisé.char[]
est moins vulnérable.Considère ceci:
Tirages:
la source
toString
isclassname@hashcode
.[C
représentechar[]
, le reste est du code de hachage hexadécimal.Password
type de classe pour cela. C'est moins obscur et plus difficile à passer accidentellement quelque part.Pour citer un document officiel, le guide de Java Cryptography architecture dit ceci au sujet par
char[]
rapport auxString
mots de passe (sur le cryptage par mot de passe, mais cela est plus généralement sur les mots de passe bien sûr):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 que ce soit à l'origine dans le contexte de la journalisation):
la source
String
est assez bien compris et comment il se comporte dans la JVM ... il y a de bonnes raisons d'utiliserchar[]
à la placeString
lorsque vous traitez les mots de passe de manière sécurisée.At which point
- c'est spécifique à l'application, mais la règle générale est de le faire dès que vous obtenez quelque chose qui est censé être un mot de passe (texte en clair ou autre). Vous l'obtenez depuis le navigateur dans le cadre d'une requête HTTP, par exemple. Vous ne pouvez pas contrôler la livraison, mais vous pouvez contrôler votre propre stockage, donc dès que vous l'obtenez, mettez-le dans un caractère [], faites ce que vous devez en faire, puis mettez tout à '0' et laissez le gc récupérer.Les tableaux de caractères (
char[]
) peuvent être effacés après utilisation en mettant chaque caractère à zéro et non aux chaînes. Si quelqu'un peut en quelque sorte voir l'image de la mémoire, il peut voir un mot de passe en texte brut si des chaînes sont utilisées, mais s'ilchar[]
est utilisé, après avoir purgé les données avec des 0, le mot de passe est sécurisé.la source
HttpServletRequest
objet en texte clair. Si la version JVM est 1.6 ou inférieure, elle sera dans l'espace permgen. S'il est en 1.7, il sera toujours lisible jusqu'à ce qu'il soit collecté. (Quand c'est le cas.)intern()
des chaînes arbitraires. Mais vous avez raison en ce que lesString
instances existent en premier lieu (jusqu'à ce qu'elles soient collectées) et les transformer ensuite enchar[]
tableaux ne change rien.new String()
ouStringBuilder.toString()
j'ai géré des applications avec beaucoup de constantes de chaîne, et nous avons eu beaucoup de fluage de permgen en conséquence. Jusqu'au 1.7.intern()
à une chaîne arbitraire pourrait provoquer l'allocation d'une chaîne équivalente dans l'espace permgen. Ce dernier pourrait se faire GC'er, s'il n'y avait pas de chaîne littérale du même contenu partageant cet objet…Certaines personnes pensent que vous devez remplacer la mémoire utilisée pour stocker le mot de passe une fois que vous n'en avez plus besoin. Cela réduit la fenêtre de temps dont dispose un attaquant pour lire le mot de passe de votre système et ignore complètement le fait que l'attaquant a déjà besoin d'un accès suffisant pour détourner la mémoire JVM pour ce faire. Un attaquant avec autant d'accès peut attraper vos événements clés, ce qui le rend complètement inutile (AFAIK, alors corrigez-moi si je me trompe).
Mise à jour
Grâce aux commentaires, je dois mettre à jour ma réponse. Apparemment, il y a deux cas où cela peut ajouter une amélioration (très) mineure de la sécurité car cela réduit le temps qu'un mot de passe peut atterrir sur le disque dur. Pourtant, je pense que c'est exagéré pour la plupart des cas d'utilisation.
Si possible, la désactivation des vidages mémoire et du fichier d'échange résoudrait les 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 la RAM d'un système en cours d'exécution serait toujours une préoccupation valable.
la source
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 vous 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 remplacer chaque élément du tableau par un blanc ou quelque chose à coup sûr. Vous ne pouvez pas modifier la valeur interne deString
cette façon.Mais cela seul n'est pas une bonne réponse; pourquoi ne pas simplement vous assurer qu'une référence au
char[]
ouString
ne s'échappe pas? Ensuite, il n'y a pas de problème de sécurité. Mais le fait est que lesString
objets peuvent êtreintern()
édités en théorie et maintenus en vie à l'intérieur du bassin constant. Je suppose que l'utilisationchar[]
interdit cette possibilité.la source
char[]
qu'elles peuvent être modifiées, et ensuite cela n'a pas d'importance qu'elles soient collectées ou non. Et puisque l'internement de chaînes doit être fait explicitement pour les non-littéraux, c'est la même chose que de dire qu'unchar[]
peut être référencé par un champ statique.La réponse a déjà été donnée, mais je voudrais partager un problème que j'ai découvert récemment avec les bibliothèques standard Java. Bien qu'ils prennent grand soin maintenant de remplacer les chaînes de mot 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 ignorées lorsqu'il s'agit de les effacer de la mémoire.Je pense par exemple à la classe PrivateKey . Considérez un scénario dans lequel vous chargeriez une clé RSA privée à partir d'un fichier PKCS # 12, en l'utilisant pour effectuer une opération. Maintenant, dans ce cas, renifler le mot de passe seul ne vous aiderait pas tant que l'accès physique au fichier de clé est correctement restreint. En tant qu'attaquant, vous feriez beaucoup mieux si vous obteniez la clé directement au lieu du mot de passe. Les informations souhaitées peuvent être des fuites multiples, des vidages de mémoire, une session de débogage ou des fichiers d'échange ne sont que quelques exemples.
Et il s'avère que rien ne vous permet d'effacer les informations privées d'un
PrivateKey
de la mémoire, car il n'y a pas d'API qui vous permet d'effacer les octets qui forment les informations correspondantes.C'est une mauvaise situation, car cet article décrit comment cette circonstance pourrait être potentiellement exploitée.
La bibliothèque OpenSSL, par exemple, écrase les sections de mémoire critiques avant de libérer les clés privées. Étant donné que Java est récupéré, 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é.
la source
PrivateKey
qui ne charge pas réellement son contenu privé en mémoire: via un jeton matériel PKCS # 11 par exemple. Peut-être qu'une implémentation logicielle de PKCS # 11 pourrait prendre en charge le nettoyage manuel de la mémoire. Il est peut-êtrePKCS11
préférable d' utiliser quelque chose comme le magasin NSS (qui partage la majeure partie de son implémentation avec le type de magasin en Java). LeKeychainStore
(magasin de clés OSX) charge le contenu complet de la clé privée dans sesPrivateKey
instances, mais il ne devrait pas en avoir besoin. (Je ne sais pas ce que fait leWINDOWS-MY
KeyStore sous Windows.)Comme le dit Jon Skeet, il n'y a pas d'autre moyen que d'utiliser la réflexion.
Cependant, si la réflexion est une option pour vous, vous pouvez le faire.
lors de l'exécution
Remarque: si le caractère de la chaîne [] a été copié dans le cadre d'un cycle GC, il est possible que la copie précédente se trouve quelque part en mémoire.
Cette ancienne copie n'apparaîtrait 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 devez éviter que quiconque ait un tel accès.
la source
'***********'
.Scanner
le tampon interne de et, comme vous ne l'avez pas utiliséSystem.console().readPassword()
, sous une forme lisible dans la fenêtre de la console. Mais pour la plupart des cas d'utilisation pratiques, la durée deusePassword
l'exécution est le problème réel. Par exemple, lors d'une connexion à une autre machine, cela prend beaucoup de temps et indique à un attaquant que c'est le bon moment pour rechercher le mot de passe dans le tas. La seule solution est d'empêcher les attaquants de lire la mémoire du tas…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 en texte brut, il sera disponible en mémoire jusqu'à ce que le garbage collector le supprime, et puisque String est utilisé dans le pool de chaînes pour être réutilisable, il y a de fortes chances qu'il le soit. rester en mémoire pendant une longue durée, ce qui constitue une menace 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, c'est une autre raison pour laquelle 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, tandis que si vous utilisez un char [], vous pouvez toujours définir tous les éléments comme vides ou nuls. Ainsi, le stockage d'un mot de passe dans un tableau de caractères atténue clairement le risque de sécurité de voler un mot de passe.
2. Java lui-même recommande d'utiliser la méthode getPassword () de JPasswordField qui retourne un char [], au lieu de la méthode obsolète getText () qui retourne les mots de passe en texte clair en indiquant des raisons de sécurité. Il est bon de suivre les conseils de l'équipe Java et de se conformer aux 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 de mémoire sera imprimé. Bien que ce ne soit pas une vraie raison, cela reste logique.
Référencé depuis ce blog . J'espère que ça aide.
la source
Edit: Pour en revenir à cette réponse après un an de recherche sur la sécurité, je me rends compte que cela implique plutôt malheureusement que vous compareriez réellement des mots de passe en texte brut. S'il vous plait, ne le faites pas. Utilisez un hachage unidirectionnel sécurisé avec un sel et un nombre raisonnable d'itérations . Pensez à utiliser une bibliothèque: ce truc est difficile à réaliser!
Réponse originale: Qu'en est-il du fait que String.equals () utilise une évaluation de court-circuit et est donc vulnérable à une attaque de synchronisation? Cela peut être peu probable, mais vous pourriez théoriquement chronométrer la comparaison des mots de passe afin de déterminer la séquence correcte de caractères.
Quelques ressources supplémentaires sur le timing des attaques:
la source
Arrays.equals
pourchar[]
est aussi élevée que pourString.equals
. Si quelqu'un s'en souciait, il y avait une classe de clé dédiée encapsulant le mot de passe réel et s'occupant des problèmes - oh attendez, les vrais packages de sécurité ont des classes de clé dédiées, cette Q&R ne concerne qu'une habitude en dehors d'eux, par exempleJPasswordField
, à utiliserchar[]
au lieu deString
(où les algorithmes réels utilisent debyte[]
toute façon).sleep(secureRandom.nextInt())
avant de rejeter une tentative de connexion de toute façon, ce qui élimine non seulement la possibilité de chronométrer les attaques, mais aussi contre les tentatives de force brute.Il n'y a rien que le tableau de caractères vous donne contre String à moins que vous ne le nettoyiez 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.
Jetez un œil à la bibliothèque Spring Security largement utilisée ici et posez-vous la question - les gars de Spring Security sont-ils incompétents ou char [] n'ont tout simplement pas beaucoup de sens. Quand un pirate méchant saisit les vidages de mémoire de votre RAM, assurez-vous qu'il obtiendra tous les mots de passe même si vous utilisez des moyens sophistiqués pour les cacher.
Cependant, Java change tout le temps, et certaines fonctionnalités effrayantes comme la fonction de déduplication des chaînes de Java 8 peuvent interner des objets String à votre insu. Mais c'est une conversation différente.
la source
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 erronées au mot de passe sur le tas ou sur le pool de chaînes. Maintenant, si quelqu'un prend un vidage de tas du processus Java et analyse attentivement, il pourrait être en mesure de 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 jeu.
D'un autre côté, les char [] sont mutables 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 prend un vidage de tas, il pourrait ne pas être en mesure d'obtenir les mots de passe qui ne sont pas actuellement utilisés. Cela vous donne plus de contrôle dans le sens comme l'effacement du contenu de l'objet vous-même par rapport à l'attente du GC pour le faire.
la source
strings
ET leString
pool (pool de chaînes précédemment utilisées) étaient LES DEUX stockés dans Permgen avant la 1.7. Voir également la section 5.1: docs.oracle.com/javase/specs/jvms/se6/html/… La machine virtuelle Java a toujours vérifiéStrings
si elles étaient la même valeur de référence et appelaitString.intern()
POUR VOUS. Le résultat était que chaque fois que la JVM détectait des chaînes identiques dans leconstant_pool
tas, elle les déplaçait dans permgen. Et j'ai travaillé sur plusieurs applications avec "creeping permgen" jusqu'à la 1.7. C'était un vrai problème.constant_pool
qui était situé dans permgen, puis si une chaîne était utilisée plusieurs fois, elle serait internée.La chaîne est immuable et va au pool de chaînes. Une fois écrit, il ne peut pas être écrasé.
char[]
est un tableau que vous devez remplacer une fois que vous avez utilisé le mot de passe et voici comment procéder:Un scénario où l'attaquant pourrait l'utiliser est un crashdump - lorsque la JVM se bloque et génère un vidage de la 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 du support qui a accès au serveur à des fins de surveillance. Il pouvait jeter un œil dans un crashdump et trouver les mots de passe.
la source
request.getPassword()
déjà la chaîne et ne l'ajoute pas au pool?ch = '0'
change la variable localech
; cela n'a aucun effet sur le tableau. Et votre exemple est inutile de toute façon, vous commencez avec une instance de chaîne que vous appeleztoCharArray()
, en créant un nouveau tableau et même lorsque vous écrasez le nouveau tableau correctement, cela ne change pas l'instance de chaîne, donc il n'a aucun avantage sur l'utilisation de la chaîne exemple.Arrays.fill(pass, '0');
La réponse courte et directe serait parce qu'il
char[]
est mutable alors que lesString
objets 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, et donc la seule façon de supprimer leur contenu de la mémoire est de les faire ramasser les ordures. Ce ne sera qu'à ce moment que la mémoire libérée par l'objet pourra être écrasée et que les données auront disparu.Désormais, le ramasse-miettes en Java ne se produit à aucun intervalle garanti. Le
String
peut donc persister en mémoire pendant une longue période et si un processus se bloque pendant ce temps, le contenu de la chaîne peut se retrouver dans un vidage de la mémoire ou un journal.Avec un tableau de caractères , vous pouvez lire le mot de passe, terminer de travailler avec lui dès que vous le pouvez, puis modifier immédiatement le contenu.
la source
La chaîne en java est immuable. Ainsi, chaque fois qu'une chaîne est créée, elle restera en mémoire jusqu'à ce qu'elle soit récupéré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, elle finira par créer une nouvelle chaîne. Ainsi, la valeur d'origine et la valeur modifiée restent en 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 l'objectif du mot de passe atteint. Le contenu d'origine du tableau ne sera pas trouvé en mémoire après sa modification et avant même que le garbage collection ne démarre.
En raison de problèmes de sécurité, il est préférable de stocker le mot de passe sous forme de tableau de caractères.
la source
Il est discutable de savoir si vous devez utiliser String ou Char [] à cet effet, car les deux ont leurs avantages et leurs inconvénients. Cela dépend de ce dont l'utilisateur a besoin.
Étant donné que les chaînes en Java sont immuables, chaque fois que certains essaient de manipuler votre chaîne, il crée un nouvel objet et la chaîne existante reste inchangée. Cela pourrait être considéré comme un avantage pour le stockage d'un mot de passe sous forme de chaîne, mais l'objet reste en mémoire même après utilisation. Donc, si quelqu'un a en quelque sorte obtenu l'emplacement de mémoire de l'objet, cette personne peut facilement retrouver votre mot de passe stocké à cet emplacement.
Char [] est modifiable, mais il a l'avantage qu'après son utilisation, le programmeur peut nettoyer explicitement le tableau ou remplacer les valeurs. Ainsi, une fois qu'il est utilisé, il est nettoyé et personne ne peut jamais connaître les informations que vous avez stockées.
Sur la base des circonstances ci-dessus, on peut se faire une idée si aller avec String ou avec Char [] pour leurs besoins.
la source
Chaîne de cas:
Affaire CHAR ARRAY:
la source