it-swarm.com.de

Unterschied zwischen Java.util.Random und Java.security.SecureRandom

Mein Team hat einen serverseitigen Code (in Java) übergeben, der zufällige Token generiert, und ich habe eine Frage zu diesem Thema -

Der Zweck dieser Token ist ziemlich empfindlich - sie wird für Sitzungs-IDs, Links zum Zurücksetzen von Kennwörtern usw. verwendet. Sie müssen daher kryptografisch zufällig sein, um zu vermeiden, dass jemand sie erraten oder sie brutal erzwingen kann. Das Token ist "lang" und hat eine Länge von 64 Bit.

Der Code verwendet derzeit die Klasse Java.util.Random, um diese Token zu generieren. In der Dokumentation ([ http://docs.Oracle.com/javase/7/docs/api/Java/util/Random.html([1] ) für Java.util.Random heißt es eindeutig: 

Instanzen von Java.util.Random sind nicht kryptografisch sicher. Verwenden Sie stattdessen SecureRandom, um einen kryptografisch sicheren Generator für Pseudozufallszahlen zur Verwendung durch sicherheitsrelevante Anwendungen zu erhalten.

Die Art und Weise, in der der Code derzeit Java.util.Random verwendet, ist jedoch Folgendes: Er instanziiert die Java.security.SecureRandom-Klasse und verwendet dann die SecureRandom.nextLong()-Methode, um den Seed zu erhalten, der zum Instantiieren der Java.util.Random-Klasse verwendet wird. Dann verwendet es die Java.util.Random.nextLong()-Methode, um das Token zu generieren.

Meine Frage jetzt - Ist es noch unsicher, wenn der Java.util.Random mit Java.security.SecureRandom ausgesät wird? Muss ich den Code so ändern, dass er ausschließlich Java.security.SecureRandom verwendet, um die Token zu generieren?

Derzeit ist der Code-Startwert die Startvariable Random

192
user967973

Die Standardimplementierung von Oracle JDK 7 verwendet einen sogenannten linearen Kongruenzgenerator, um Zufallswerte in Java.util.Random zu erzeugen.

Entnommen aus dem Quellcode von Java.util.Random (JDK 7u2) aus einem Kommentar zur Methode protected int next(int bits), die die Zufallswerte generiert:

Dies ist ein linearer kongruenter Pseudozufallszahlengenerator, wie er von D. H. Lehmer definiert und von Donald E. Knuth in beschrieben wird Die Kunst der Computerprogrammierung, Band 3: Seminumerische AlgorithmenAbschnitt 3.2.1.

Vorhersagbarkeit linearer Kongruenzgeneratoren

Hugo Krawczyk hat eine ziemlich gute Abhandlung darüber geschrieben, wie diese LCGs vorhergesagt werden können ("Wie man kongruente Generatoren vorhersagt"). Wenn Sie Glück haben und interessiert sind, finden Sie möglicherweise immer noch eine kostenlose, herunterladbare Version davon im Internet. Und es gibt viel mehr Nachforschungen, die eindeutig belegen, dass Sie niemals ein LCG für sicherheitskritische Zwecke verwenden sollten. Dies bedeutet auch, dass Ihre Zufallszahlen derzeit vorhersehbar sind, was Sie für Sitzungs-IDs und dergleichen nicht wünschen.

So brechen Sie einen linearen Kongruenzgenerator

Die Annahme, dass ein Angreifer warten müsste, bis sich die LCG nach einem vollständigen Zyklus wiederholt, ist falsch. Selbst mit einem optimalen Zyklus (dem Modul m in seiner Wiederholungsrelation) ist es sehr einfach, zukünftige Werte in viel kürzerer Zeit als in einem vollständigen Zyklus vorherzusagen. Schließlich müssen nur ein paar modulare Gleichungen gelöst werden, was einfach wird, sobald Sie genügend Ausgabewerte der LCG beobachtet haben.

Die Sicherheit verbessert sich nicht mit einem "besseren" Samen. Es ist einfach egal, ob Sie einen Zufallswert setzen, der von SecureRandom generiert wurde, oder den Wert sogar durch mehrmaliges Würfeln erzeugen.

Ein Angreifer berechnet einfach den Startwert aus den beobachteten Ausgabewerten. Dies dauert deutlich weniger Zeit als 2 ^ 48 im Fall von Java.util.Random. Ungläubige können dies ausprobieren Experiment , wo gezeigt wird, dass Sie zukünftige Random Ausgaben vorhersagen können, wobei Sie nur zwei (!) Ausgabewerte in einer Zeit von ungefähr 2 ^ 16 beobachten. Auf einem modernen Computer dauert es nicht einmal eine Sekunde, um die Ausgabe Ihrer Zufallszahlen vorherzusagen.

Fazit

Ersetzen Sie Ihren aktuellen Code. Verwenden Sie ausschließlich SecureRandom. Dann haben Sie zumindest eine kleine Garantie dafür, dass das Ergebnis schwer vorherzusagen ist. Wenn Sie die Eigenschaften eines kryptografisch sicheren PRNG (in Ihrem Fall das, was Sie wollen) möchten, müssen Sie nur SecureRandom wählen. Wenn Sie klug darüber sind, die Art und Weise zu ändern, wie es verwendet werden sollte, führt dies fast immer zu weniger Sicherheit ...

228
emboss

Ein Random hat nur 48 Bit, wobei SecureRandom bis zu 128 Bit haben kann. Daher sind die Chancen, sich in securerandom zu wiederholen, sehr gering.

Random verwendet den system clock als Samen/oder um den Samen zu erzeugen. Sie können also leicht reproduziert werden, wenn der Angreifer den Zeitpunkt kennt, zu dem der Samen erzeugt wurde. SecureRandom nimmt jedoch Random Data von Ihrer os (sie kann ein Intervall zwischen den Tastenanschlägen usw. sein - die meisten sammeln diese Daten in Dateien - /dev/random and /dev/urandom in case of linux/solaris) und verwenden diese als Startwert. 
Wenn also die kleine Tokengröße in Ordnung ist (im Fall von Random), können Sie Ihren Code ohne Änderungen weiterverwenden, da Sie den Seed mit SecureRandom generieren. Wenn Sie jedoch größere Token wünschen (die nicht brute force attacks unterliegen können), gehen Sie zu SecureRandom - 
Bei zufälligen Hinweisen sind nur 2^48-Versuche erforderlich. Mit den heutigen fortschrittlichen CPU-Versionen ist es möglich, sie in der praktischen Zeit zu unterbrechen. Für das Securerandom werden jedoch 2^128-Versuche erforderlich sein, die mit den modernen Maschinen von heute viele Jahre brauchen werden.

Siehe this link für weitere Details .
EDIT
Nachdem Sie die von @emboss bereitgestellten Links gelesen haben, ist es klar, dass der Samen, egal wie zufällig er auch ist, Nicht mit Java.util.Random verwendet werden sollte. Es ist sehr einfach, den Samen durch Beobachten der Ausgabe zu berechnen.

Go for SecureRandom - Verwenden Sie Native PRNG (wie im obigen Link angegeben), da für jeden Aufruf an nextBytes() zufällige Werte aus der Datei /dev/random verwendet werden. Auf diese Weise kann ein Angreifer, der die Ausgabe beobachtet, nichts erkennen, es sei denn, er kontrolliert den Inhalt der /dev/random-Datei (was sehr unwahrscheinlich ist). 
Der sha1 prng -Algorithmus berechnet den Startwert nur einmal und wenn Ihr VM monatelang mit demselben Startwert läuft, könnte er von einem Angreifer geknackt werden, der die Ausgabe passiv beobachtet.

NOTE - Wenn Sie die nextBytes() schneller aufrufen, als Ihr Betriebssystem in der Lage ist, zufällige Bytes (Entropie) in den /dev/random zu schreiben, können Sie bei Verwendung von NATIVE PRNG. Verwenden Sie in diesem Fall eine SHA1 PRNG - Instanz von SecureRandom und samen Sie diese Instanz alle paar Minuten (oder in einem bestimmten Intervall) mit dem Wert von nextBytes() einer NATIVE PRNG - Instanz von SecureRandom. Wenn Sie diese beiden parallel ausführen, wird sichergestellt, dass Sie regelmäßig mit echten Zufallswerten aussäen, ohne dabei die vom Betriebssystem erhaltene Entropie zu erschöpfen.

68
Ashwin

Wenn Sie Java.util.Random.nextLong() zweimal mit dem gleichen Startwert ausführen, wird dieselbe Nummer erzeugt. Aus Sicherheitsgründen möchten Sie bei Java.security.SecureRandom bleiben, da dies viel weniger vorhersehbar ist.

Die 2 Klassen sind ähnlich, ich denke, Sie müssen nur Random in SecureRandom mit einem Refactoring-Tool ändern und der größte Teil Ihres vorhandenen Codes wird funktionieren.

8
Mualig

Wenn das Ändern Ihres vorhandenen Codes eine erschwingliche Aufgabe darstellt, sollten Sie die SecureRandom-Klasse verwenden, wie in Javadoc vorgeschlagen. 

Auch wenn Sie feststellen, dass die Random-Klasse die SecureRandom-Klasse intern verwendet. Sie sollten es nicht als selbstverständlich betrachten: 

  1. Andere VM -Implementierungen machen dasselbe. 
  2. Die Implementierung der Random-Klasse in zukünftigen Versionen des JDK verwendet weiterhin die SecureRandom-Klasse 

Daher ist es eine bessere Wahl, dem Dokumentationsvorschlag zu folgen und direkt mit SecureRandom zu gehen.

3
Andrea Parodi

Der Same ist bedeutungslos. Ein guter Zufallsgenerator unterscheidet sich in der gewählten Primzahl. Jeder Zufallsgenerator beginnt mit einer Zahl und durchläuft einen 'Ring'. Das heißt, Sie kommen mit dem alten internen Wert von einer Zahl zur nächsten. Aber nach einer Weile erreicht man wieder den Anfang und fängt von vorne an. Sie laufen also Zyklen. (der Rückgabewert eines Zufallsgenerators ist nicht der interne Wert)

Wenn Sie zum Erstellen eines Rings eine Primzahl verwenden, werden alle Nummern in diesem Ring ausgewählt, bevor Sie einen vollständigen Zyklus durch alle möglichen Nummern abschließen. Wenn Sie Nicht-Primzahlen verwenden, werden nicht alle Zahlen ausgewählt und Sie erhalten kürzere Zyklen.

Höhere Primzahlen bedeuten längere Zyklen, bevor Sie zum ersten Element zurückkehren. Der sichere Zufallsgenerator hat also einen längeren Zyklus, bevor er wieder an den Anfang gelangt, weshalb er sicherer ist. Sie können die Zahlengenerierung nicht so einfach vorhersagen wie bei kürzeren Zyklen.

Mit anderen Worten: Sie müssen alle ersetzen.

2
Nicolas

Die aktuelle Referenzimplementierung von Java.util.Random.nextLong() ruft die Methode next(int) auf, die direkt 32 Bit des aktuellen Seed verfügbar macht:

protected int next(int bits) {
    long nextseed;
    // calculate next seed: ...
    // and store it in the private "seed" field.
    return (int)(nextseed >>> (48 - bits));
}

public long nextLong() {
    // it's okay that the bottom Word remains signed.
    return ((long)(next(32)) << 32) + next(32);
}

Das obere 32-Bit des Ergebnisses von nextLong() sind die Bits des Seed zu diesem Zeitpunkt. Da die Seedbreite 48 Bit beträgt (sagt der Javadoc), genügt es *, die verbleibenden 16 Bit (das sind nur 65.536 Versuche) zu iterieren, um den Seed zu bestimmen, der die zweiten 32 Bit erzeugt.

Sobald der Samen bekannt ist, können alle nachfolgenden Token leicht berechnet werden.

Durch die direkte Ausgabe von nextLong() wird teilweise das Geheimnis der PNG in einem Maße erreicht, dass das gesamte Geheimnis mit sehr wenig Aufwand berechnet werden kann. Gefährlich!

* Wenn das zweite 32-Bit negativ ist, ist etwas Aufwand erforderlich, aber das kann man herausfinden.

1
Matt

Ich werde versuchen, sehr einfache Wörter zu verwenden, damit Sie den Unterschied zwischen Random und SecureRandom und der Bedeutung von SecureRandom Class leicht verstehen können.

Haben Sie sich jemals gefragt, wie OTP (Einmalkennwort) generiert wird? Um ein OTP zu generieren, verwenden wir ebenfalls die Random- und die SecureRandom-Klasse. Um Ihre OTP stark zu machen, ist SecureRandom jetzt besser, weil es 2 ^ 128 Versuche dauerte, um die OTP zu knacken, was mit dem aktuellen Rechner fast unmöglich ist. Wenn jedoch Random Class verwendet wird, kann Ihre OTP von jemandem geknackt werden, der Ihre Daten beschädigen kann, weil er es braucht nur 2 ^ 48 versuchen, zu knacken. 

0
sachin pathak