Mein Team hat einen serverseitigen Code (in Java) erhalten, der zufällige Token generiert, und ich habe eine Frage dazu -
Der Zweck dieser Token ist ziemlich sensibel - sie werden für Sitzungs-IDs, Links zum Zurücksetzen von Passwörtern usw. verwendet. Sie müssen also kryptografisch zufällig sein, um zu vermeiden, dass jemand sie errät oder brutal erzwingt. Das Token ist "lang", also 64 Bit lang.
Der Code verwendet derzeit die java.util.Random
Klasse, um diese Token zu generieren. In der Dokumentation zu heißt es java.util.Random
eindeutig:
Instanzen von java.util.Random sind nicht kryptografisch sicher. Verwenden Sie stattdessen SecureRandom, um einen kryptografisch sicheren Pseudozufallszahlengenerator für sicherheitsrelevante Anwendungen zu erhalten.
Der Code verwendet derzeit jedoch Folgendes java.util.Random
: Er instanziiert die java.security.SecureRandom
Klasse und verwendet dann die SecureRandom.nextLong()
Methode, um den Startwert zu erhalten, der zum Instanziieren der java.util.Random
Klasse verwendet wird. Anschließend java.util.Random.nextLong()
wird das Token mithilfe der Methode generiert.
Also meine Frage jetzt - Ist es immer noch unsicher, wenn man bedenkt, dass das java.util.Random
mit ausgesät wird java.security.SecureRandom
? Muss ich den Code so ändern, dass er java.security.SecureRandom
ausschließlich zum Generieren der Token verwendet wird?
Derzeit ist der Code-Seed der Random
einmalige Start
quelle
Random
beim Start einmal oder setzt er für jedes Token einen neuen? Hoffentlich ist das eine dumme Frage, aber ich dachte, ich würde es überprüfen.long
oderdouble
Werte.Antworten:
Die Standardimplementierung von Oracle JDK 7 verwendet einen sogenannten linearen Kongruenzgenerator, um zufällige Werte in zu erzeugen
java.util.Random
.Entnommen aus dem
java.util.Random
Quellcode (JDK 7u2), aus einem Kommentar zur Methodeprotected int next(int bits)
, die die Zufallswerte generiert:Vorhersagbarkeit linearer Kongruenzgeneratoren
Hugo Krawczyk hat ein ziemlich gutes Papier 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 noch viele weitere Untersuchungen, die eindeutig zeigen, dass Sie ein LCG niemals für sicherheitskritische Zwecke verwenden sollten. Dies bedeutet auch, dass Ihre Zufallszahlen derzeit vorhersehbar sind , was Sie für Sitzungs-IDs und dergleichen nicht möchten.
So brechen Sie einen linearen Kongruenzgenerator
Die Annahme, dass ein Angreifer nach einem vollständigen Zyklus auf die Wiederholung der LCG warten müsste, 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 ein vollständiger Zyklus vorherzusagen. Schließlich müssen nur einige modulare Gleichungen gelöst werden, was einfach wird, sobald Sie genügend Ausgabewerte des LCG beobachtet haben.
Die Sicherheit verbessert sich nicht mit einem "besseren" Samen. Es spielt einfach keine Rolle, ob Sie mit einem zufälligen Wert säen
SecureRandom
, der von einem Würfel erzeugt wird, oder diesen sogar durch mehrmaliges Würfeln erzeugen.Ein Angreifer berechnet einfach den Startwert aus den beobachteten Ausgabewerten. Dies dauert im Fall von deutlich weniger Zeit als 2 ^ 48
java.util.Random
. Ungläubige können dieses Experiment ausprobieren , bei dem gezeigt wird, dass Sie zukünftigeRandom
Ausgaben vorhersagen können , indem Sie nur zwei (!) Ausgabewerte in der 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
SecureRandom
ausschließlich. Dann haben Sie zumindest eine kleine Garantie dafür, dass das Ergebnis schwer vorherzusagen ist. Wenn Sie die Eigenschaften eines kryptografisch sicheren PRNG wünschen (in Ihrem Fall ist es das, was Sie wollen), müssen Sie nur mit gehenSecureRandom
. Wenn Sie klug sind, die Art und Weise zu ändern, wie es verwendet werden sollte, führt dies fast immer zu etwas weniger Sicherem ...quelle
Random
es kaputt ist - es sollte nur in verschiedenen Szenarien verwendet werden. Natürlich können Sie jederzeit SecureRandom verwenden. Aber im AllgemeinenSecureRandom
ist merklich langsamer als reinRandom
. Und es gibt Fälle, in denen Sie nur an guten statistischen Eigenschaften und einer hervorragenden Leistung interessiert sind, sich aber nicht wirklich um die Sicherheit kümmern: Monte-Carlo-Simulationen sind ein gutes Beispiel. Ich habe dies in einer ähnlichen Antwort kommentiert , vielleicht finden Sie es nützlich.Ein Zufall hat nur 48 Bit, während SecureRandom bis zu 128 Bit haben kann. Die Wahrscheinlichkeit, sich im Securerandom zu wiederholen, ist daher sehr gering.
Random verwendet das
system clock
als Startwert / oder, um den Startwert zu generieren. Sie können also leicht reproduziert werden, wenn der Angreifer den Zeitpunkt kennt, zu dem der Samen erzeugt wurde. Aber SecureRandom nimmtRandom Data
von Ihremos
(sie können Intervalle zwischen Tastenanschlägen usw. sein - die meisten Betriebssysteme sammeln diese Daten, speichern sie in Dateien -/dev/random and /dev/urandom in case of linux/solaris
) und verwendet dies als Startwert.Wenn die kleine Tokengröße in Ordnung ist (im Fall von Random), können Sie Ihren Code ohne Änderungen weiter verwenden, da Sie SecureRandom zum Generieren des Seeds verwenden. Wenn Sie jedoch größere Token möchten (die nicht unterliegen können
brute force attacks
), wählen Sie SecureRandom -Wenn nur zufällige
2^48
Versuche erforderlich sind, ist es mit den heutigen fortgeschrittenen CPUs möglich, diese in der praktischen Zeit zu brechen. Für die Sicherung2^128
sind jedoch zufällige Versuche erforderlich, die Jahre und Jahre dauern werden, um mit den heutigen fortschrittlichen Maschinen die Gewinnschwelle zu erreichen.Siehe diesen Link für weitere Details.
BEARBEITEN
Nach dem Lesen der von @emboss bereitgestellten Links ist klar, dass der Startwert, wie zufällig er auch sein mag, nicht mit java.util.Random verwendet werden sollte. Es ist sehr einfach, den Startwert durch Beobachtung der Ausgabe zu berechnen.
Entscheiden Sie sich für SecureRandom - Verwenden Sie natives PRNG (wie im obigen Link angegeben), da
/dev/random
für jeden Aufruf von zufällige Werte aus der Datei verwendet werdennextBytes()
. Auf diese Weise kann ein Angreifer, der die Ausgabe beobachtet, nur dann etwas erkennen, wenn er den Inhalt der/dev/random
Datei kontrolliert (was sehr unwahrscheinlich ist).Der sha1 prng- Algorithmus berechnet den Startwert nur einmal und wenn Ihre VM monatelang mit derselben ausgeführt wird Samen, es könnte von einem Angreifer geknackt werden, der die Ausgabe passiv beobachtet.
HINWEIS - Wenn Sie
nextBytes()
schneller anrufen , als Ihr Betriebssystem zufällige Bytes (Entropie) in das schreiben kann/dev/random
, kann es bei Verwendung von NATIVE PRNG zu Problemen kommen . Verwenden Sie in diesem Fall eine SHA1 PRNG-Instanz von SecureRandom und setzen Sie diese Instanz alle paar Minuten (oder in einem bestimmten Intervall) mit dem Wert vonnextBytes()
einer NATIVE PRNG-Instanz von SecureRandom. Wenn Sie diese beiden Optionen parallel ausführen, wird sichergestellt, dass Sie regelmäßig mit echten Zufallswerten säen, ohne die vom Betriebssystem erhaltene Entropie zu erschöpfen.quelle
Random
, das OP sollte überhaupt nicht verwendet werdenRandom
./proc/sys/kernel/random/entropy_avail
und überprüfen Sie mit einigen Thread-Dumps, dass beim Lesen nicht zu lange gewartet wird/dev/random
Wenn Sie zweimal
java.util.Random.nextLong()
mit demselben Samen laufen , wird dieselbe Zahl erzeugt. Aus Sicherheitsgründen möchten Sie dabei bleiben,java.security.SecureRandom
da dies viel weniger vorhersehbar ist.Die 2 Klassen sind ähnlich. Ich denke, Sie müssen nur mit einem Refactoring-Tool wechseln
Random
,SecureRandom
und der größte Teil Ihres vorhandenen Codes funktioniert.quelle
Wenn das Ändern Ihres vorhandenen Codes eine kostengünstige Aufgabe ist, empfehlen wir Ihnen, die in Javadoc vorgeschlagene SecureRandom-Klasse zu verwenden.
Selbst wenn Sie feststellen, dass die Implementierung der Random-Klasse die SecureRandom-Klasse intern verwendet. Sie sollten es nicht als selbstverständlich ansehen, dass:
Es ist daher eine bessere Wahl, dem Dokumentationsvorschlag zu folgen und direkt mit SecureRandom zu arbeiten.
quelle
java.util.Random
ImplementierungSecureRandom
intern verwendet wird, sondern dass ihr Code verwendet wird,SecureRandom
um die zu setzenRandom
. Trotzdem stimme ich beiden bisherigen Antworten zu; Es ist am besten,SecureRandom
eine explizit deterministische Lösung zu vermeiden.Die aktuelle Referenzimplementierung von
java.util.Random.nextLong()
führt zwei Aufrufe der Methode durch,next(int)
die 32 Bit des aktuellen Startwerts direkt verfügbar macht :Die oberen 32 Bit des Ergebnisses von
nextLong()
sind die Bits des Startwerts zu der Zeit. Da die Breite des Startwerts 48 Bit beträgt (sagt der Javadoc), reicht es aus, * über die verbleibenden 16 Bit zu iterieren (das sind nur 65,536 Versuche), um den Startwert zu bestimmen, der die zweiten 32 Bit erzeugt hat.Sobald der Samen bekannt ist, können alle folgenden Token leicht berechnet werden.
Unter Verwendung der Ausgabe von
nextLong()
direkt, teilweise dem Geheimnis des PNG in einem Ausmaß, dass das gesamte Geheimnis mit sehr wenig Aufwand berechnet werden kann. Gefährlich!* Es sind einige Anstrengungen erforderlich, wenn die zweiten 32 Bit negativ sind, aber das kann man herausfinden.
quelle
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 erreichen Sie wieder den Anfang und beginnen von vorne. 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 Zahlen in diesem Ring ausgewählt, bevor Sie einen vollständigen Zyklus durch alle möglichen Zahlen 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 wieder zum ersten Element zurückkehren. Der sichere Zufallsgenerator hat also nur einen längeren Zyklus, bevor er wieder am Anfang ankommt. Deshalb ist er sicherer. Sie können die Zahlengenerierung nicht so einfach vorhersagen wie bei kürzeren Zyklen.
Mit anderen Worten: Sie müssen alle ersetzen.
quelle
Ich werde versuchen, sehr einfache Wörter zu verwenden, damit Sie den Unterschied zwischen Random und SecureRandom und die Bedeutung der SecureRandom-Klasse leicht verstehen können.
Haben Sie sich jemals gefragt, wie OTP (Einmalpasswort) generiert wird? Um ein OTP zu generieren, verwenden wir auch die Klassen Random und SecureRandom. Um Ihr OTP jetzt stark zu machen, ist SecureRandom besser, da es 2 ^ 128 Versuche dauerte, das OTP zu knacken, was auf dem gegenwärtigen Computer fast unmöglich ist. Wenn Sie jedoch Random Class verwenden, kann Ihr OTP von jemandem geknackt werden, der Ihre Daten beschädigen kann, weil es nötig war nur 2 ^ 48 versuchen, zu knacken.
quelle