Différence entre java.util.Aléatoire et java.de sécurité.SecureRandom
Mon équipe a transmis certains de côté de serveur de code (en Java) qui génère aléatoirement des jetons et j'ai une question concernant le même
Le but de ces jetons est assez sensible - utilisé pour l'id de session, réinitialisation de mot de passe de liens etc. Donc, ils ne doivent être chiffrée de manière aléatoire pour éviter que quelqu'un de deviner ou de la force brute eux faisable. Le jeton est un "long" de sorte qu'il est de 64 bits.
Le code utilise actuellement le java.util.Random
classe pour générer ces jetons. La documentation ([http://docs.oracle.com/javase/7/docs/api/java/util/Random.html][1]) pour java.util.Random
clairement les états suivants:
Cas de java.util.Aléatoire ne sont pas cryptographique sécurisé. Plutôt considérer l'aide SecureRandom pour obtenir un point de vue cryptographique sécurisé générateur de nombres pseudo aléatoires pour une utilisation par les services de sécurité des applications sensibles.
Cependant, la façon dont le code est actuellement à l'aide de java.util.Random
est-ce - qu'Il instancie la java.security.SecureRandom
classe et utilise ensuite la SecureRandom.nextLong()
méthode pour obtenir de la semence qui est utilisé pour l'instanciation de la java.util.Random
classe. Ensuite, il utilise java.util.Random.nextLong()
méthode pour générer le jeton.
Donc ma question maintenant: Est - il toujours incertain étant donné que la java.util.Random
est alimentée à l'aide de java.security.SecureRandom
? Ai-je besoin de modifier le code pour qu'il utilise java.security.SecureRandom
exclusivement pour générer les jetons?
Actuellement le code de la graine de la Random
fois au démarrage
- Une fois semées, la sortie de java.util.Le hasard est déterministe de la séquence de nombres. Vous ne voudrez pas que.
- Le code de la graine de la
Random
fois au démarrage, ou de graines pour chaque jeton? J'espère que ce est une question stupide, mais j'ai pensé que je voudrais vérifier. - Random a seulement 48 bits interne de l'état et de répéter après 2^48 appels à nextLong (), ce qui signifie qu'il ne produira pas possible
long
oudouble
valeurs. - Il y a un autre problème grave. 64bits moyens de 1,84*10^19 combinaisons possibles, ce qui est trop peu pour résister à une attaque sophistiquée. Il y a des machines qui fêlé 56 bits DES code (facteur de 256 moins) avec 90*10^9 clés par seconde en 60 heures. L'utilisation de 128 bits ou deux longs !
Vous devez vous connecter pour publier un commentaire.
Le standard d'Oracle JDK 7 mise en œuvre utilise ce qu'on appelle un Générateur Linéaire à Congruence pour produire des valeurs aléatoires dans
java.util.Random
.Prises de
java.util.Random
code source (JDK 7u2), à partir d'un commentaire sur la méthodeprotected int next(int bits)
, qui est celui qui génère les valeurs aléatoires:La prévisibilité de Congruence Linéaire Générateurs
Hugo Krawczyk a écrit un très bon papier sur la façon dont ces LCGs peut être prédite ("Comment prévoir des générateurs à congruence"). Si vous avez de la chance et intéressés, vous pouvez toujours en trouver un gratuit, version téléchargeable sur le web. Et il y a beaucoup plus de recherche montre clairement que vous devriez jamais utiliser une GRILLE pour la sécurité des fins de critique. Cela signifie aussi que vos nombres aléatoires sont prévisible dès maintenant, quelque chose que vous ne voulez pas pour les Id de session et la comme.
Comment casser un Générateur Linéaire à Congruence
L'hypothèse qu'un attaquant pourrait avoir à attendre pour le LCG à répéter après un cycle complet est mauvais. Même avec une optimale du cycle (le module m dans sa relation de récurrence), il est très facile de prédire les valeurs futures en beaucoup moins de temps qu'un cycle complet. Après tout, c'est juste un tas d'équations modulaires qui doivent être résolus, ce qui devient facile dès que vous avez observé assez les valeurs de sortie de la GRILLE.
La sécurité ne s'améliore pas avec un "meilleur" de la graine. Il n'a tout simplement pas d'importance si vous les graines avec une valeur aléatoire générée par
SecureRandom
ou même produire de la valeur par laminage d'une mourir plusieurs fois.Un attaquant simplement calculer la graine à partir de la sortie des valeurs observées. Cela prend nettement moins de temps que 2^48 dans le cas de
java.util.Random
. Les mécréants peuvent essayer ce expérience, où il est montré que vous pouvez prédire l'avenir de l'Random
sorties d'observation de deux(!) les valeurs de sortie dans le temps d'environ 2^16. Il prend même pas une seconde sur un ordinateur moderne pour prédire la sortie de votre nombres aléatoires droit maintenant.Conclusion
Remplacer votre code actuel. Utilisation
SecureRandom
exclusivement. Au moins vous aurez un peu de garantir que le résultat sera difficile à prédire. Si vous voulez les propriétés d'un point de vue cryptographique sécurisé PRNG (dans votre cas, c'est ce que vous voulez), alors vous devez aller avecSecureRandom
seulement. Faire le malin sur la modification de la façon dont il était censé être utilisé entraîneront presque toujours quelque chose de moins sûr...Random
est cassé, il faut juste être utilisés dans des scénarios différents. Bien sûr, vous pouvez toujours utiliser SecureRandom. Mais en général,SecureRandom
est sensiblement plus lent que pureRandom
. Et il y a des cas où vous êtes seulement intéressés par de bonnes propriétés statistiques et d'excellentes performances, mais vous n'avez pas vraiment se soucier de la sécurité: simulations Monte-Carlo sont un bon exemple. J'ai fait des commentaires à ce sujet dans un similaire réponse, peut-être que vous le trouverez utile.Un hasard a seulement 48 bits où, comme SecureRandom peut avoir jusqu'à 128 bits. Ainsi, les chances de répéter dans securerandom est très faible.
Aléatoire utilise le
system clock
que les semences/ou à produire de la semence. Ils peuvent donc être reproduits facilement si l'attaquant sait l'heure à laquelle la semence a été produite. Mais SecureRandom prendRandom Data
de votreos
(ils peuvent être de l'intervalle entre deux frappes au clavier etc - la plupart des os de recueillir ces données, de les stocker dans des fichiers -/dev/random and /dev/urandom in case of linux/solaris
) et l'utilise comme la graine.Si la petite taille de jeton est d'accord(dans le cas de l'Aléatoire), vous pouvez continuer à utiliser votre code sans aucun changement, puisque vous utilisez SecureRandom pour générer de la graine. Mais si vous voulez plus de jetons(qui ne peut pas être soumis à
brute force attacks
) aller avec SecureRandom -En cas de random juste
2^48
tentatives sont nécessaires, avec aujourd'hui avancé de la cpu, il est possible de le briser en un temps pratique. Mais pour securerandom2^128
tentatives seront nécessaires, ce qui prendra des années et des années de briser, même aujourd'hui avec des machines de pointe.Voir cette lien pour plus de détails.
MODIFIER
Après avoir lu les liens fournis par @relief, il est clair que la semence, cependant aléatoire, il peut-être,
ne doit pas être utilisé avec java.util.Aléatoire. Il est très facile de calculer la graine en observant la sortie.
Aller pour SecureRandom - Utilisation Natif PRNG (comme indiqué dans le lien ci-dessus), parce qu'il prend des valeurs aléatoires à partir de la
/dev/random
fichier pour chaque appel ànextBytes()
. De cette façon, un attaquant observant la sortie ne sera pas en mesure de faire quelque chose à moins qu'il contrôle le contenu de la/dev/random
fichier(ce qui est très rare)Le sha1 prng algorithme calcule la graine qu'une seule fois, et si votre machine virtuelle est en cours d'exécution pour les mois à l'aide de la même graine, il pourrait être forcé par un attaquant qui est passivement observant la sortie.
NOTE - Si vous appelez le
nextBytes()
plus vite que votre os est en mesure d'écrire des octets aléatoires(entropie) dans le/dev/random
, vous risquez de terre en difficulté lors de l'utilisation de NATIF PRNG. Dans ce cas, utiliser un SHA1 PRNG instance de SecureRandom et toutes les quelques minutes(ou quelques intervalle), graines de cette instance avec la valeur denextBytes()
d'un INDIGÈNE PRNG instance de SecureRandom. L'exécution de ces deux rapprochement permettra d'assurer que vous formons régulièrement avec de vraies valeurs aléatoires, tout en épuisant pas l'entropie obtenue par le Système d'Exploitation.Random
, l'OP ne devraient pas utiliser desRandom
à tous./proc/sys/kernel/random/entropy_avail
et de vérifier avec du fil décharges qu'il n'est pas trop longue attente lors de la lecture sur/dev/random
Si vous exécutez deux fois
java.util.Random.nextLong()
avec la même graine, il va produire le même nombre. Pour des raisons de sécurité, vous voulez coller avecjava.security.SecureRandom
parce qu'il est beaucoup moins prévisible.Les 2 Classes sont similaires, je pense que vous avez juste besoin de changer
Random
àSecureRandom
avec un outil de refactoring et de la plupart de votre code fonctionne.Si la modification de votre code existant est un prix abordable de la tâche, je vous suggère d'utiliser le SecureRandom classe comme suggéré dans la Javadoc.
Même si vous trouvez que le Hasard de la classe de mise en œuvre utilise le SecureRandom classe interne. vous ne devriez pas prendre pour acquis que:
Il est donc un meilleur choix de suivre la documentation de suggestion et d'aller directement avec SecureRandom.
java.util.Random
de mise en œuvre utilisésSecureRandom
en interne, il a dit leur code utiliseSecureRandom
pour les semences de laRandom
. Encore, je suis d'accord avec à la fois les réponses jusqu'à présent; il est préférable d'utiliserSecureRandom
pour éviter expressément une solution déterministe.La référence actuelle de la mise en œuvre de
java.util.Random.nextLong()
fait deux appels à la méthodenext(int)
qui directement expose 32 bits de la graine:La partie supérieure de 32 bits de la suite de
nextLong()
sont les bits de la graine à l'époque. Étant donné que la largeur de la graine est de 48 bits (dit la javadoc), il suffit* pour itérer sur les autres 16 bits (c'est seulement 65.536 tente) pour déterminer la graine qui a produit le deuxième 32 bits.Une fois que la graine est connu, tous les symboles suivants peuvent être facilement calculés.
L'aide de la sortie de
nextLong()
directement, en partie le secret de la PNG à un degré que tout le secret peut être calculée avec très peu d'efford. Dangereux!* Il y a des efforts nécessaires si les 32 bits sont négatives, mais on peut le trouver.
La graine est dénuée de sens. Un bon générateur aléatoire diffère dans le choisi primenumber. Chaque générateur aléatoire démarre à partir d'un nombre et parcourt un "ring". Ce qui signifie, vous venez d'un numéro à l'autre, avec l'ancienne valeur interne. Mais après un certain temps, vous atteignez le début et tout recommencer. Si vous exécutez des cycles. (la valeur de retour à partir d'un générateur aléatoire n'est pas la valeur interne)
Si vous utilisez un nombre premier pour la création d'un anneau, tous les numéros de cette bague choisit, avant de vous achever un cycle complet par le biais de tous les nombres possibles. Si vous prenez non des nombres premiers, pas tous les numéros sont choisis et vous obtenez des cycles plus courts.
Plus élevé des nombres de la prime moyenne, des cycles plus longs, avant votre retour au premier élément nouveau. Donc, le secure générateur de hasard, juste a un cycle plus long, avant d'atteindre le début, c'est pourquoi il est plus sûr. Vous ne pouvez pas prédire le nombre de génération aussi facile qu'avec des cycles plus courts.
Avec d'autres mots: il faut les remplacer tous.
Je vais essayer d'utiliser des mots de base, de sorte que vous pouvez facilement comprendre la différence entre le Hasard et secureRandom et de l'importance de SecureRandom Classe.
Jamais demandé comment OTP(one time password) est généré?
Pour générer un OTP nous aussi utiliser Aléatoire et SecureRandom classe. Maintenant, pour faire de votre OTP forte, SecureRandom est mieux car il a fallu 2^128 essayer, pour faire craquer le bureau du procureur qui est presque impossible par l'actuelle machine, mais si utilisé Aléatoire de Classe alors votre OTP peut être forcé par quelqu'un qui peut nuire à vos données, car il a fallu seulement 2^48 essayer, à se fissurer.