it-swarm.com.de

Warum ist Math.random () nicht kryptografisch sicher?

Die JavaScript Math.random() -Funktion gibt einen einzelnen IEEE-Gleitkommawert n zurück, sodass 0 ≤ n <1. Es ist (oder sollte zumindest allgemein bekannt sein), dass die Ausgabe nicht kryptografisch sicher ist. Die meisten modernen Implementierungen verwenden den XorShift128 + -Algorithmus, der leicht beschädigt werden kann . Da es nicht ungewöhnlich ist, dass Benutzer es fälschlicherweise verwenden, wenn sie eine bessere Zufälligkeit benötigen, warum ersetzen Browser es nicht durch ein CSPRNG? Ich weiß, dass Opera das * zumindest macht. Der einzige Grund, den ich mir vorstellen könnte, wäre, dass XorShift128 + schneller als ein CSPRNG ist, aber auf modernen (und auch nicht so modernen) Computern wäre es trivial, Hunderte von Megabyte pro Sekunde mit ChaCha8 oder AES-CTR auszugeben. Diese sind häufig schnell genug, sodass eine gut optimierte Implementierung möglicherweise nur durch die Speichergeschwindigkeit des Systems eingeschränkt wird. Selbst eine nicht optimierte Implementierung von ChaCha20 ist auf allen Architekturen extrem schnell, und ChaCha8 ist mehr als doppelt so schnell.

Ich verstehe, dass es nicht als CSPRNG neu definiert werden konnte, da der Standard ausdrücklich keine Garantie für die Eignung für die kryptografische Verwendung gibt, aber es scheint keinen Nachteil für Browser-Anbieter zu geben, die dies freiwillig tun. Dies würde die Auswirkung von Fehlern in einer großen Anzahl von Webanwendungen verringern, ohne gegen den Standard zu verstoßen (es ist nur erforderlich, dass die Ausgabe auf geradlinige IEEE 754 -Nummern gerundet wird), was abnimmt Leistung oder Unterbrechung der Kompatibilität mit Webanwendungen.


BEARBEITEN: Einige Leute haben darauf hingewiesen, dass dies möglicherweise dazu führen kann, dass Leute diese Funktion missbrauchen, selbst wenn der Standard besagt, dass Sie sich für die kryptografische Sicherheit nicht darauf verlassen können. Meiner Meinung nach gibt es zwei gegensätzliche Faktoren, die bestimmen, ob die Verwendung eines CSPRNG einen Nettosicherheitsvorteil darstellt oder nicht:

  1. Falsches Sicherheitsgefühl - Die Anzahl der Personen, die andernfalls eine für diesen Zweck entwickelte Funktion verwenden würden, z. B. window.crypto , entscheiden Sie sich stattdessen für Math.random(), da es auf der vorgesehenen Zielplattform kryptografisch sicher ist.

  2. Opportunistische Sicherheit - Die Anzahl der Personen, die es nicht besser wissen und Math.random() ohnehin für vertrauliche Anwendungen verwenden, vor denen sie geschützt wären ihr eigener Fehler. Natürlich wäre es besser, sie stattdessen zu erziehen, aber das ist nicht immer möglich.

Man kann davon ausgehen, dass die Anzahl der Personen, die vor ihren eigenen Fehlern geschützt wären, die Anzahl der Personen, die in ein falsches Sicherheitsgefühl versetzt werden, bei weitem übersteigen würde.

* Wie CodesInChaos hervorhebt, gilt dies jetzt nicht mehr, da Opera basiert auf Chromium).


Mehrere große Browser haben Fehlerberichte erhalten, in denen vorgeschlagen wurde, diese Funktion durch eine kryptografisch sichere Alternative zu ersetzen, aber keine der vorgeschlagenen sicheren Änderungen ist gelandet:

Die Argumente für die Änderung stimmen im Wesentlichen mit meinen überein. Die Argumente dagegen variieren von reduzierter Leistung auf Mikrobenchmarks (mit geringen Auswirkungen in der realen Welt) bis zu Missverständnissen und Mythen, wie der falschen Vorstellung, dass ein CSPRNG mit der Zeit schwächer wird, wenn mehr Zufälligkeit erzeugt wird. Am Ende erstellte Chromium ein völlig neues Kryptoobjekt, und Firefox ersetzte sein RNG durch den XorShift128 + -Algorithmus. Die Funktion Math.random() bleibt vollständig vorhersehbar.

228
forest

Ich war Mitte bis Ende der neunziger Jahre einer der Implementierer von JScript und Mitglied des ECMA-Ausschusses, daher kann ich hier eine historische Perspektive geben.

Die JavaScript-Funktion Math.random () gibt einen Gleitkommawert zwischen 0 und 1 zurück. Es ist allgemein bekannt (oder sollte es zumindest sein), dass die Ausgabe nicht kryptografisch sicher ist

Zunächst einmal: Das Design vieler RNG-APIs ist schrecklich . Die Tatsache, dass die .NET Random-Klasse trivial auf mehrere Arten missbraucht werden kann, um lange Sequenzen derselben Nummer zu erzeugen, ist schrecklich. Eine API, bei der die natürliche Verwendung auch die falsche ist, ist eine "Pit of Failure" -API. Wir möchten, dass unsere APIs Erfolgsgruben sind, in denen der natürliche und der richtige Weg gleich sind.

Ich denke, es ist fair zu sagen, dass wenn wir damals wüssten, was wir jetzt wissen, die JS-Zufalls-API anders wäre. Selbst einfache Dinge wie das Ändern des Namens in "Pseudozufall" würden helfen, da, wie Sie bemerken, in einigen Fällen die Implementierungsdetails von Bedeutung sind. Auf architektonischer Ebene gibt es gute Gründe, warum Sie möchten, dass random() eine Factory ist, die ein Objekt zurückgibt, das eine zufällige oder pseudozufällige Sequenz darstellt, anstatt einfach Zahlen zurückzugeben. Und so weiter. Gewonnene Erkenntnisse.

Zweitens wollen wir uns daran erinnern, was der grundlegende Entwurfszweck von JS in den neunziger Jahren war. Lassen Sie den Affen tanzen, wenn Sie die Maus bewegen . Wir haben Inline-Ausdrucksskripte als normal angesehen, wir haben zwei- bis zehnzeilige Skriptblöcke als üblich angesehen, und die Vorstellung, dass jemand ein hundert Zeilen Skript auf eine Seite schreiben könnte, war wirklich sehr ungewöhnlich. Ich erinnere mich an das erste Mal, als ich ein JS-Programm mit zehntausend Zeilen sah, und meine erste Frage an die Leute, die mich um Hilfe baten, weil es im Vergleich zu ihrer C++ - Version so langsam war, war eine Version von "Bist du verrückt?! 10KLOC JS?!" ""

Die Vorstellung, dass jeder Krypto-Zufälligkeit benötigen würde in JS, war ähnlich verrückt. Sie brauchen Ihre Affenbewegungen, um eine unvorhersehbare Kryptostärke zu haben? Unwahrscheinlich.

Denken Sie auch daran, dass es Mitte der neunziger Jahre war. Wenn Sie nicht dafür da waren, kann ich Ihnen sagen, dass es eine ganz andere Welt als heute war, was Krypto betrifft ... Siehe Export von Kryptographie .

Ich hätte nicht einmal in Betracht gezogen Krypto-Stärke-Zufälligkeit in irgendetwas gesteckt, das mit dem Browser geliefert wurde, ohne eine große Menge an Rechtsberatung vom MSLegal-Team zu erhalten. Ich wollte Krypto nicht mit einer zehn Fuß langen Stange in einer Welt berühren, in der der Versandcode in Betracht gezogen wurde , um Munition an Staatsfeinde zu exportieren . Das klingt aus heutiger Sicht verrückt, aber das war die Welt, die war .

warum ersetzen Browser es nicht durch ein CSPRNG?

Browser-Autoren müssen keinen Grund angeben, KEINE Änderungen vorzunehmen. Änderungen kosten Geld und nehmen den Aufwand für bessere Änderungen weg. Jede Änderung hat enorme Opportunitätskosten.

Vielmehr müssen Sie nicht nur argumentieren, warum die Änderung eine gute Idee ist, sondern auch, warum sie ihre Zeit bestmöglich nutzt. Dies ist eine Kleinigkeit für den Geldbeutel.

Ich verstehe, dass es nicht als CSPRNG neu definiert werden konnte, da der Standard ausdrücklich keine Garantie für die Eignung für die kryptografische Verwendung gibt, aber es scheint keinen Nachteil zu geben, dies trotzdem zu tun

Der Nachteil ist, dass Entwickler immer noch in einer Situation sind, in der sie nicht zuverlässig wissen können, ob ihre Zufälligkeit Kryptostärke ist oder nicht, und noch leichter in die Falle geraten können, sich auf eine Eigenschaft zu verlassen, die es nicht ist durch den Standard garantiert. Die vorgeschlagene Änderung behebt das Problem nicht, bei dem es sich um ein Entwurfsproblem handelt.

443
Eric Lippert

Weil es tatsächlich eine kryptografisch sichere Alternative zu Math.random() gibt:

window.crypto.getRandomValues(typedArray)

Dadurch kann der Entwickler das richtige Tool für den Job verwenden. Wenn Sie hübsche Bilder oder Beutetropfen für Ihr Spiel generieren möchten, verwenden Sie die schnelle Funktion Math.random(). Wenn Sie kryptografisch sichere Zufallszahlen benötigen, verwenden Sie das teurere window.crypto.

116
Philipp

JavaScript (JS) wurde 1995 erfunden.

  1. Potenziell illegal: Die Kryptografie war 1995 noch unter strenger Exportkontrolle, sodass die Verbreitung eines guten CSPRNG in einem Browser möglicherweise nicht einmal legal war.
  2. Leistung: Historisch gesehen sind CSPRNGs (kryptografisch sichere Pseudozufallszahlengeneratoren) viel langsamer als PRNGs. Warum also standardmäßig ein CSPRNG verwenden?
  3. Keine Sicherheitseinstellung: 1995 wurde SSL gerade erfunden. Es wurden praktisch keine Server unterstützt, das Internet bestand aus Telefonleitungen und wurde für öffentliche Foren ( BBSes ) und MUDs verwendet. Verschlüsselung war etwas für Geheimdienste.
  4. Keine Notwendigkeit: Webanwendungen existierten noch nicht, da JavaScript gerade erfunden wurde. Es wurde als interpretierte (also langsame) Sprache entwickelt, um Seiten dynamischer zu machen. Es wäre niemandem in den Sinn gekommen, standardmäßig ein langsames (und kaum vorhandenes) CSPRNG für eine Zufallsfunktion zu verwenden.
  5. In der Tat so wenig Bedarf, dass es keine Alternative gab: JavaScript hatte bis Dezember 2013 nicht einmal eine allgemein unterstützte API für ein CSPRNG, also richtige Kryptographie in Webanwendungen war bis vor einigen Jahren kaum möglich.
  6. Konsistenz: Anstatt eine vorhandene Funktion so zu ändern, dass sie eine andere Bedeutung hat, haben sie eine neue Funktion mit einem anderen Namen erstellt. Sie können jetzt über crypto.getRandomValues Auf das CSPRNG zugreifen.

Zusammenfassend: Vermächtnis, aber auch Geschwindigkeit und Konsistenz . Unsichere PRNGs sind immer noch viel schneller, da Sie nicht davon ausgehen können, dass die gesamte Hardware AES-Unterstützung bietet, und auch nicht von der Verfügbarkeit oder Sicherheit von RDRAND abhängen.

Persönlich denke ich, dass es Zeit ist, alle zufälligen Funktionen mit CSPRNGs auszutauschen und die schnelleren, unsicheren Funktionen in etwas wie fast_insecure_random() umzubenennen. Sie sollten nur von Wissenschaftlern oder anderen Personen benötigt werden, die Simulationen durchführen, die viele Zufallszahlen benötigen, bei denen die Vorhersagbarkeit des RNG jedoch keine Rolle spielt. Aber für eine Funktion mit zwei Jahrzehnten Geschichte, in der es erst seit vier Jahren (2018) eine Alternative gibt, kann ich verstehen, warum wir noch nicht an diesem Punkt sind.

69
Luc

Dies ist zu lang für einen Kommentar.

Ich glaube, Ihre Frage enthält eine fehlerhafte Prämisse:

auf modernen (und auch nicht so modernen) Computern wäre es trivial, Hunderte von Megabyte pro Sekunde mit ChaCha8 oder AES-CTR auszugeben

Sie denken an einen Desktop-Browser auf einem mit Wechselstrom verbundenen Computer oder einen Laptop mit einem großen 10-Ah-Akku von Honkin.

Wir leben in einer zunehmend mobilen Welt, und obwohl mobile Geräte heutzutage ziemlich leistungsfähig sind, gibt es zwei wichtige Einschränkungen: Wärme und Akkulaufzeit. Im Gegensatz zu einem Desktop-Prozessor, der leicht 100 ° C erreichen kann, können Sie dem Smartphone-Benutzer nicht die Hand verbrennen. Und Telefonbatterien halten normalerweise nur vielleicht 1/3 so viel wie ein Laptop (wenn Sie Glück haben). Es gibt einfach keinen guten Grund, die zusätzliche Wärmeerzeugung/den zusätzlichen Stromverbrauch hinzuzufügen, wenn Sie ihn nicht benötigen.

20
Jared Smith

Der größere Grund ist, dass es eine Alternative zu Math.random() gibt: siehe Philipps Antwort . Wer also eine starke Krypto benötigt, kann sie haben, und diejenigen, die dies nicht tun, können Zeit und (Batterie-) Energie sparen.

Angenommen, Sie hätten gefragt: "Warum haben die Entwickler, selbst wenn es eine stärkere Alternative gibt, Math.random () nicht trotzdem aktualisiert - dh random () zu einer Ableitung von getRandomValues ​​() gemacht -, um automatisch zu stärken viele Apps da draußen? " - dann denke ich nicht, dass dies wirklich mit Sicherheit beantwortet werden kann, außer von den Entwicklern, die die Entscheidung getroffen haben (Update: und wie es das Schicksal wollte, wir haben eine solche Antwort ).

Grundsätzlich gibt es - wie Sie bereits sagten - keinen starken Grund .

Darüber hinaus haben die meisten Entwicklungsteams einen erheblichen Rückstand an Dingen, die dringender sind. und selbst eine scheinbar kleine Änderung wie diese erfordert Testen, Regression und Verstoß gegen die goldene Regel " Wenn sie nicht gebrochen ist, beheben Sie sie nicht", eine stärkere Form des YAGNI-Kriteriums .

16
LSerni

Zufallszahlen und Kryptozufallsbits sind völlig unterschiedliche Tiere. Sie werden nicht einmal für den gleichen Zweck verwendet. Wenn Sie eine Zufallszahl möchten, die gleichmäßig zwischen 0 und 42 verteilt ist, möchten Sie eine gleichmäßige Verteilung ohne offensichtliches Muster. Beachten Sie, dass es keine gerade Verteilung ist, wenn Sie eine größere Zahl mit einer kleineren modifizieren. Dieses Beispiel ist für eine Zufallszahl von 0 bis 31 aus Mod 27 leicht zu erkennen. 0 bis 4 werden doppelt so oft angezeigt wie 5 bis 31.

Bis Sie über Kryptozufall sprechen, wird der Begriff der Entropie nicht einmal diskutiert. Ein bisschen Entropie verdoppelt den Suchraum, um die Nummer zu erraten (für den beabsichtigten Benutzer der Nummern).

Wenn Sie nach Kryptozufallsbits fragen, fragen Sie nach N Entropiebits. Es ist nicht gut genug, ein nicht offensichtliches Muster zu haben, denn wenn eine Funktion entdeckt wird, die es erzeugt (egal wie verworren), dann gibt es tatsächlich 0 Entropiebits aus der Sicht desjenigen, der diese Funktion kennt.

Ein gutes Beispiel hierfür ist ein Fortuna-ähnlicher Pseudozufallszahlengenerator. Sie verschlüsseln eine Nummer 1 mit einem Schlüssel für die erste Zufallszahl (wobei der Verschlüsselungsblock eine große Zahl ist), verschlüsseln dann Nummer 2 mit einem Schlüssel für die zweite Zufallszahl und so weiter. In Bezug auf den Benutzer, der den Schlüssel (von K Bits) zur Verschlüsselung nicht kennt, weist ein perfekter N-Bit-Verschlüsselungsblock N Entropiebits für diesen Block auf.

Wenn Sie eine Million Bits pseudozufälliger Daten daraus erweitern, haben Sie immer noch nur K Entropiebits, wenn Sie mit demselben Schlüssel K weitermachen. Mit anderen Worten: Wenn Sie ein Buch mit 1 Million Bits hatten, das Sie haben Wenn Sie wissen, dass Sie mit einer einzelnen Verschlüsselung unter K generiert wurden, versuchen Sie nicht, alle Verschlüsselungsstrombits zu erraten. Halten Sie sich einfach an das Erraten des Schlüssels und generieren Sie den Chiffrestream daraus.

Ein Zufallszahlengenerator ist also oft eine Chiffre, die immer wieder mit mehr Zufälligkeit neu ausgesät wird, wie es möglich ist. Im Vergleich dazu kann ein einfacher [0,1] Zufallszahlengenerator nicht mehr Entropie haben als die Anzahl der Bits in der Zahl. und hat normalerweise eine ungerade Verteilung, die nicht genau das ist, was Sie auch wollen. Krypto benötigt Hunderte von Bits, wenn Gleitkommazahlen nur 32 oder 64 Bit sind, und der Algorithmus selbst nimmt einen Großteil der Entropie weg ... vorausgesetzt, Sie möchten etwas, das gleichmäßig von [0..1] verteilt ist, anstatt a zu sagen Gleitkomma-Darstellung aus zufälligen Bits. Ich weiß nicht einmal, welche Verteilung das überhaupt haben würde.

14
Rob

Sie haben die Frage irgendwie selbst beantwortet:

der Standard gibt ausdrücklich keine Garantie für die Eignung für die kryptografische Verwendung

Anstatt die Implementierung zu ändern, sollte der Schwerpunkt darauf liegen, die Entwickler darin zu schulen, das „richtige Werkzeug für den Job“ ™ auszuwählen.

Angesichts dessen und des technischen Aufwands bei der Änderung der Implementierung einer häufig verwendeten Funktion sowie der Tatsache, dass es bereits spezifische Lösungen für dieses Problem gibt (siehe Antwort von @Philipps), gibt es keinen zwingenden Grund, die Änderung vorzunehmen.

8
richzilla

Das Design von Programmiersprachen muss viele Dinge berücksichtigen. Browser sind sehr leistungsfähig und optimieren Javascript heute sehr. Wenn Sie jedoch eingebettete Systeme in Betracht ziehen, haben Sie möglicherweise keine gute Zufallsquelle. Zum Beispiel gibt es Mikrocontroller, die eine NodeJS (ähnliche) Umgebung ausführen.

Ein solcher Mikrocontroller hat keine Zufallsquellen, die kryptografisch sichere Zufallszahlen garantieren. Sie müssten also ein Gerät anschließen, das eine zufällige Eingabe an einen Pin liefern kann, um eine Programmiersprache implementieren zu können, die starke Garantien für Zufallszahlen bietet. Und Sie benötigen einige Kenntnisse, um ein Gerät zu erstellen, das genügend Zufälligkeit bietet, und um die Eingaben vom Gerät in geeigneter Weise zu verarbeiten.

5
allo

Wie andere möchte ich darauf hinweisen, dass Math.random() nicht kryptografisch sicher ist, da es normalerweise nicht benötigt wird. Aber ich würde noch weiter gehen und argumentieren, dass es nicht ratsam ist, einen kryptografisch sicheren Algorithmus in eine Spezifikation zu schreiben, es sei denn, Sie haben einen sehr guten Grund.

Was bedeutet es, kryptografisch sicher zu sein? Nun, es gibt immer die langweilige Definition von "Niemand weiß, wie man es noch bricht". Aber was passiert, wenn jemand es kaputt macht? Wenn Sie ein CSPRNG angegeben haben, müssen Sie auch eine Möglichkeit angeben, um abzufragen, welcher Algorithmus verwendet wird, oder auf andere Weise festlegen, dass der Endbenutzer sicher sein kann, was er erhält.

Dies führt wahrscheinlich auch dazu, dass mehrere Generatoren unterstützt werden müssen, damit der Benutzer einen auswählen kann, dem er vertraut. Dies erhöht die Komplexität erheblich. Plötzlich wurde eine 1-Zeilen-Funktion in einer API zu einer Suite.

Wenn Sie anfangen, über Krypto zu sprechen, sprechen Sie auch über den Versuch, im Generator sicher zu sein. Sie erwähnen die Verwendung von AES zur Generierung von Zufallszahlen: Muss Ihre AES-Implementierung gegen Seitenkanalangriffe immun sein? Wenn Sie eine Bibliothek mit dem speziellen Zweck schreiben, kryptografische Garantien bereitzustellen, ist es nicht allzu unvernünftig, diese Frage stellen zu müssen. Für eine Spezifikation könnte es schrecklich unvernünftig sein. Die Immunität gegen Seitenkanalangriffe ist eine sehr schwer zu formulierende Sache in der Sprache der Spezifikationen.

Und was haben Sie erreicht, indem Sie es in eine Spezifikation aufgenommen haben? Die meisten Benutzer von PRNGs benötigen überhaupt keine kryptografischen Garantien, sodass Sie nur CPU-Zyklen für sie verschwendet haben. Diejenigen, die kryptografische Garantien wünschen, werden wahrscheinlich eine Bibliothek suchen, die die gesamte Palette an Funktionen unterstützt, die erforderlich sind, um mit einer solchen Kryptografie vertraut zu sein, sodass sie Math.random() sowieso nicht vertrauen. Alles, was übrig bleibt, ist die von Ihnen erwähnte Bevölkerungsgruppe: Menschen, die einen Fehler gemacht und ein Werkzeug verwendet haben, wenn sie es nicht sollten. Nun, ich kann Ihnen aus Erfahrung sagen, dass wichtige Programmiersprachen nicht ein Ort sind, an dem Sie nach einer API suchen können, die Sie nicht versehentlich falsch verwenden können. Sie sind voll von Formulierungen "Wenn du das tust, ist es deine eigene Schuld".

Beachten Sie auch Folgendes: Wenn Sie Math.random() verwenden und kryptografische Garantien annehmen, wie hoch ist die Wahrscheinlichkeit, dass Sie irgendwo in Ihrem Algorithmus einen weiteren schwerwiegenden Kryptofehler machen? Ein CSPRNG Math.random() kann ein falsches Sicherheitsgefühl vermitteln, und wir können noch mehr Fehler finden!

5
Cort Ammon

Jeder scheint hier eine Nuance übersehen zu haben: Kryptografische Algorithmen erfordern, dass eine Zahl über alle Ausführungen des Algorithmus mathematisch und statistisch zufällig ist. Dies bedeutet zum Beispiel während eines Spiels oder einer Animation, dass Sie eine pseudozufällige Folge von Zahlen verwenden könnten, und dies wäre für eine "Art von Zufallszahl" vollkommen in Ordnung.

Wenn diese Zahl jedoch manipuliert oder vorhergesagt werden könnte, beispielsweise eine gesetzte Zufallszahl (was das Standardverhalten der Windows-Zufallsfunktionen ist), ist dieser Start tatsächlich vorhersehbar. Wenn ich Ihre Anwendung manipulieren kann, um neu zu starten und dann eine gesetzte Zufallszahl zu verwenden, kann ich vorhersagen, welche "Zufallszahl" Sie wählen werden. Wenn dies möglich ist, kann die Kryptographie besiegt werden. Ein sekundäres Problem kann auch sein, dass einige Algorithmen eine garantierte Verteilung von Zahlen über das Spektrum erfordern, die bestimmte Pseudozufallsgeneratoren nicht garantieren können.

Kryptografisch Zufallszahlengeneratoren verfügen über eine große Anzahl von Eingängen, um Entropie zu erzeugen, z. B. das Messen des Schussrauschens vom Mikrofoneingang, der Uhrzeit, der Prüfsumme der RAM-Register, der Seriennummern usw. So viele Eingaben wie möglich, um dies zu verhindern, wenn nicht unmöglich. dann unglaublich schwer zu manipulieren und vorherzusagen. In einem kryptografischen Sinne ist Leistung nicht das Ziel, sondern "wahre" Zufälligkeit.

Abhängig von Ihrem Anwendungsfall möchten Sie möglicherweise eine einigermaßen zufällige, performante Implementierung einer Zufallszahl. Wenn Sie jedoch einen Diffie-Hellman-Schlüsselaustausch durchführen, benötigen Sie einen kryptografisch sicheren Algorithmus.

3
Spence