39 votes

Sécurité Java: comment effacer / mettre à zéro la mémoire associée à un objet? (Et / ou assurez-vous que c'est la seule instance / copie d'une variable particulière)

Je suis dans une discussion à travailler sur la façon de sécuriser les informations sensibles (par exemple des mots de passe) stockées dans un programme Java. Par les exigences de sécurité, de mémoire contenant des informations sensibles est désactivée, par exemple en fixant les valeurs des octets à tous les zéros. Le souci est qu'un attaquant peut observer la mémoire associés au processus de demande, et nous voulons donc de limiter autant que possible la fenêtre de temps de telles informations sensibles traîne. Auparavant, les projets C++, donc un memset() suffit.

(Incidemment, l'utilisation de memset() a été appelée en cause par certains compilateurs sont connus pour optimiser l'utilisation de l'exécutable résultant basée sur l'hypothèse que, puisque la mémoire n'est pas utilisée plus tard, il n'est pas nécessaire à zéro dans la première place. Ce texte de présentation est un avertissement pour ceux qui Google pour "memset" et "effacer la mémoire", etc).

Maintenant que nous avons sur nos mains un projet Java étant pressé contre cette exigence.

Pour les objets Java, ma compréhension, c'est que:

  • un entaché de nullité de référence ne change la valeur de la référence; la mémoire sur le tas pour l'objet contient encore des données
  • un objet immuable comme la Chaîne ne serait pas en mesure d'avoir des données modifiées (ou du moins pas facilement, dans les limites d'une machine virtuelle avec de bien activé dans le gestionnaire de sécurité)
  • la génération éboueurs peut faire des copies d'objets de tous sur la place (comme indiqué ici)

Et pour les primitives, ma compréhension, c'est que:

  • une primitive de type variable locale de la méthode sont alloués sur la pile, et:
  • lorsque vous modifiez une valeur, de le modifier directement dans la mémoire (par opposition à l'aide d'une référence à manipuler un objet sur le tas).
  • copies/serait faite "derrière les coulisses", dans certaines situations, comme en passant, comme un argument en méthodes ou de boxe (automatique ou non) la création des instances de l'emballage, qui contient une autre primitive variable contenant la même valeur.

Mon collègue affirme que Java primitives sont immuables et qu'il y a de la documentation à partir à la fois de la NSA, et Oracle à propos de l'absence de soutien en Java pour répondre à cette exigence.

Ma position est que les primitives peuvent (au moins dans certains cas) être remis à zéro par le réglage de la valeur à zéro (ou booléen à false), et la mémoire est effacée de cette façon.

Je suis en train de vérifier s'il y a dans la langue dans la JLS ou autres "officiel" de la documentation sur le comportement requis de Jvm quand il s'agit de la gestion de la mémoire à l'égard de primitives. Le plus proche que j'ai pu trouver était "Sûr des Directives de Codage pour le Langage de Programmation Java" sur Oracle site qui mentionne la compensation des tableaux de char après utilisation.

J'avais chipoter sur des définitions lorsque mon collègue appelées primitives immuable, mais je suis sûr qu'il voulait dire "la mémoire ne peut pas être correctement remis à zéro" - nous allons vous inquiétez pas à ce sujet. Nous n'avons pas à discuter si il voulait final variables de contexte, on parlait en général.

Existe-il des réponses définitives ou des références à ce sujet? Je te remercie de tout ce qui pourrait me montrer où je me trompe ou confirmer que j'ai raison.

Edit: Après réflexion, j'ai été en mesure de préciser que mon collègue a été pensée de la primitive wrappers, pas les primitifs eux-mêmes. Donc on se retrouve avec le problème de comment libérer de la mémoire en toute sécurité, de préférence des objets. Aussi, pour clarifier, les informations sensibles ne sont pas juste des mots de passe, mais aussi des choses comme des adresses IP ou des clés de chiffrement.

Y a-commercial machines virtuelles qui offrent une fonctionnalité comme une priorité à la manipulation de certains objets? (J'imagine que ce serait en fait violer la Java spec, mais j'ai pensé que je demanderais juste au cas où je me trompe.)

4voto

Voo Points 11981

Edit: en Fait j'ai juste eu trois idées qui peuvent en effet travailler - pour différentes valeurs de "travail" au moins.

La première qui est plus ou moins documentées serait ByteBuffer.allocateDirect! Ce que je comprends allocateDirect alloue de la mémoire tampon à l'extérieur de l'habitude java heap ne sera donc pas copié autour de. Je ne peux pas trouver tout dur garantit de ne pas arriver copié dans toutes les situations, mais pour l'actuel Hotspot VM c'est effectivement le cas (c'est à dire qu'il est attribué à un segment de mémoire), et je suppose qu'il va rester de cette façon.

La deuxième est l'utilisation du soleil.misc.dangereux paquet - qui comme son nom l'indique, a des problèmes évidents, mais au moins ce serait quasiment indépendante de la VM - soit il est pris en charge (et ça marche), ou il n'est pas (et vous obtenez les erreurs de liaison). Le problème, c'est que le code à utiliser ce genre de choses obtiendrez horriblement compliquée assez rapide (seul obtenir un dangereux variable est non trivial).

La troisième serait d'allouer beaucoup, beaucoup, beaucoup plus grande taille que ce qui est réellement nécessaire, de sorte que l'objet est attribué à l'ancienne génération tas pour commencer:

l-XX:PretenureSizeThreshold= qui peut être configuré pour limiter l' la taille des allocations dans les jeunes génération. L'affectation de plus de ce ne sera pas tenté de faire dans le les jeunes de la génération et sera donc d'attribution de l'ancienne génération.

Bien l'inconvénient de CETTE solution est évidente, je crois (taille par défaut semble être d'environ 64 ko).

. .

De toute façon, ici, la vieille réponse:

Yep comme je vois qu'il vous est impossible de garantir que les données stockées sur le tas est 100% enlevée sans laisser une copie (c'est vrai même si vous ne voulez pas une solution générale, mais celui qui va travailler avec les dire de l'actuel Hotspot VM et son défaut ramasseurs d'ordures).

Comme dit dans votre message lié (ici), le garbage collector assez bien fait cet impossible à garantir. En fait contrairement à ce que le post dit que le problème ici n'est pas de la génération GC, mais le fait que le Hotspot VM (et maintenant, nous sommes de mise en œuvre spécifique) est à l'aide de une sorte de Stop & Copy gc de sa jeune génération par défaut.

Cela signifie que dès la collecte des ordures qui se passe entre le stockage du mot de passe dans le char tableau et la réinitialisation de ce que vous obtenez une copie des données qui seront remplacées uniquement dès que le GC suivant arrive. Notez que tenuring un objet aura exactement le même effet, mais au lieu de le copier à de l'espace, il est copié à l'ancienne génération de tas - nous nous retrouver avec une copie des données à partir de l'espace qui n'est pas écrasé.

Pour éviter ce problème, nous avons serais assez bien besoin d'une certaine manière à garantir qu'AUCUN de collecte des ordures qui se passe entre le stockage du mot de passe et de mise à zéro OU que le char tableau est stocké de l'aller dans l'ancienne génération de tas. Notez également qu'il s'appuie sur la internas de la zone d'accès VM qui peut très bien changer (en fait, il existe différents ramasseurs d'ordures, où de nombreux exemplaires supplémentaires peuvent être générés; iirc le Hotspot VM prend en charge simultanée GC à l'aide d'un train de l'algorithme). "heureusement" il est impossible de garantir soit l'un de ceux-là (autant que je sache, chaque appel de la méthode/de retour présente un point fort!), donc, vous n'avez même pas tenté d'essayer (surtout que je ne vois pas de moyen de vérifier que le JIT n'est pas d'optimiser la réinitialisation de là) ;)

Semble que la seule façon de garantir que les données sont stockées dans un seul endroit est d'utiliser la JNI pour elle.

PS: Note que, bien que le ci-dessus ne s'applique que sur le Tas, vous ne pouvez pas garantir quelque chose de plus pour la pile (l'équipe probable optimiser écrit sans lit de la pile à l'écart, de sorte que lorsque vous êtes de retour de la fonction, les données seront toujours sur la pile)

3voto

bmargulies Points 49855

Dites à vos collègues qu'il s'agit d'une cause sans espoir. Qu'en est-il des tampons de socket du noyau, juste pour commencer.

Si vous ne pouvez pas empêcher les programmes indésirables d’espionner la mémoire de votre ordinateur, les mots de passe sont compromis. Période.

2voto

bwawok Points 5102

Bizarre, jamais pensé quelque chose comme cela.

Ma première idée serait de faire un char[100] pour stocker votre mot de passe dans. Mettre en il y, pour quoi, et puis de faire une boucle pour chaque char à vide.

Le problème est que le mot de passe à un certain moment se transformer en une Chaîne de caractères à l'intérieur de la base de données pilote, qui pourrait vivre dans la mémoire de 0 à l'infini secondes.

Ma deuxième idée serait d'avoir l'authentification de tout faire grâce à une sorte de JNI appel à C, mais ce serait très difficile si vous essayez d'utiliser quelque chose comme JDBC....

-1voto

user2583872 Points 11

J'ai essayé de travailler sur certains des problèmes similaires avec des informations d'identification.

Jusqu'à présent, ma seule réponse est "ne pas utiliser de chaînes à tous pour les secrets". Les cordes sont confortables à utiliser et à stocker en termes humains, mais les ordinateurs peuvent bien travailler avec des tableaux d'octets. Même le chiffrement des primitives de travailler avec byte[].

Lorsque vous n'avez pas besoin du mot de passe plus le, il vous suffit de remplir le tableau avec des zéros et ne pas laisser les GC à inventer de nouvelles manières de réutiliser vos secrets.

Dans un autre thread (Pourquoi pas de chaînes mutable en Java et .NET?) ils font l'hypothèse qu'elle est très courte vue. Que les cordes sont immuables pour des raisons de sécurité; ce qui n'a pas été conçue est-ce pas toujours les problèmes opérationnels sont les seuls dans l'existence et que la sécurité a parfois besoin d'un peu de souplesse et/ou de soutien pour être efficace, un support n'existe pas en Java en natif.

Pour compléter. Comment pourrions-nous lire un mot de passe sans l'aide de cordes? Eh bien ... soyez créatif et ne pas utiliser des choses comme les Android EditText avec le type d'entrée de mot de passe, qui n'est tout simplement pas assez sécurisé et vous oblige à aller à cordes.

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