une - taille tableau java




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

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

Pourquoi String pose-t-il une menace à la sécurité en matière de mots de passe? Il n'est pas pratique d'utiliser char[] .


  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. Comme les chaînes sont utilisées dans le pool String pour être réutilisées, il y a de fortes chances pour qu'il reste en mémoire longtemps. 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. Recommandation Java utilisant la méthode getPassword() 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'imprimer du 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.


Bien que d’autres suggestions semblent valables, il existe une autre bonne raison. Avec Plain String vous avez beaucoup plus de chances d’ imprimer accidentellement le mot de passe dans des journaux , des écrans ou dans un autre lieu 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: [[email protected]

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 dont dispose un attaquant pour lire le mot de passe de votre système et ignore totalement le fait qu'il a déjà besoin d'un accès suffisant pour détourner la mémoire de la machine virtuelle Java. Un attaquant avec autant 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 apporter 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 l’assumer 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’accéder au matériel - en utilisant des outils tels que TrueCrypt (discontinued), 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 l'extraction de la mémoire RAM d'un système en cours de fonctionnement resterait une préoccupation valable.


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 de GC, il est possible 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 toute personne ayant un tel accès.


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 des caractères 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 à la classe PrivateKey . 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. Maintenant, 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 restreint. 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 il se trouve que 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 paper 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é.


Les tableaux de caractères ( char[] ) peuvent être effacés après utilisation en définissant chaque caractère à zéro et non à Chaîne. Si quelqu'un peut en quelque sorte 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é.


Edit: Pour en revenir à cette réponse après un an de recherche sur la sécurité, je me rends bien compte que cela implique l’implication plutôt regrettable que vous compareriez jamais les mots de passe en clair. S'il vous plaît ne pas. Utilisez un hachage unidirectionnel sécurisé avec un sel et un nombre raisonnable d’itérations . Pensez à utiliser une bibliothèque: il est difficile d'obtenir ce matériel!

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 temporelle? Cela peut sembler improbable, mais vous pouvez théoriquement synchroniser la comparaison de mot de passe afin de déterminer la séquence correcte de caractères.

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        // Quits here if Strings are different lengths.
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            // Quits here at first different character.
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

Quelques ressources supplémentaires sur les attaques temporelles:


Les cordes sont immuables . Cela signifie qu'une fois que vous avez créé la String , si un autre processus peut vider la mémoire, il est impossible (en dehors de la reflection ) de supprimer les données avant que la récupération de place ne démarre.

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, c'est un problème de sécurité - mais même utiliser char[] ne fait que réduire la fenêtre d'opportunité pour un attaquant, et uniquement 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 pense que cela est spécifique à l'implémentation - le ramasse-miettes peut vider 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 caractère char[] contient les caractères réels en tant que fenêtre d'attaque.


Il n'y a rien que le 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.

Jetez un coup d'œil à la bibliothèque Spring Security largement utilisée here et posez-vous la question suivante: les mots de passe Spring Security sont-ils incompétents ou les mots de passe char [] ne font-ils pas sens. Lorsqu'un méchant pirate récupère la mémoire de votre RAM, assurez-vous qu'elle 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, peuvent entraîner l'internalisation d'objets String à votre insu. Mais c'est une conversation différente.


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

Stringsen 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 que lorsque la mémoire libérée par l'objet pourra être écrasée et que les données disparaîtront.

Désormais, la récupération de place en Java ne se produit pas à un intervalle garanti. Le Stringpeut ainsi rester en mémoire pendant longtemps 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 tableau de caractères , vous pouvez lire le mot de passe, finir de le manipuler dès que vous le pouvez, puis en modifier immédiatement le contenu.


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 toutes deux 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é en 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 la forme d'un tableau de caractères.


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 et 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 = null;
 }
}

Un scénario dans lequel l'attaquant pourrait l'utiliser est un vidage sur incident (lorsque la machine virtuelle Java 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 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.





char