- Go 1.22 stellt die standardmäßige Zufallsquelle von
math/randundmath/rand/v2auf einen kryptografisch starken Generator um und reduziert damit deutlich den Schaden, wenn versehentlichmath/randan Stellen verwendet wird, an denencrypto/randnötig wäre - Der bisherige Go-1-Generator ist ein Linear Feedback Shift Register mit 607
uint64-Zuständen; schon aus 607 beobachteten Ausgaben lassen sich frühere und zukünftige Werte rekonstruieren - PCG-DXSM in
math/rand/v2verbessert die statistische Zufallsqualität und die Zustandsgröße, garantiert aber nicht die für Geheimwerte nötige Unvorhersagbarkeit - Der neue ChaCha8Rand nutzt einen 32-Byte-Seed, Rekeying alle 16 Blöcke und 300 Byte Zustand pro Core; er wird in
math/rand/v2, Teilen vonmath/randsowie für Map-Hash-Seeds eingesetzt - Die Performance-Kosten sind begrenzt: ChaCha8Rand ist langsamer als der Go-1-Generator, aber höchstens um den Faktor 2, und auf typischen Servern beträgt der Unterschied nicht mehr als 3 ns, sodass der Sicherheitsgewinn für die meisten Programme überwiegt
Was Go 1.22 an den Zufalls-Defaults ändert
- Go 1.22 ändert die Defaults in
math/randundmath/rand/v2so, dass ein kryptografisch starker Pseudozufallszahlengenerator verwendet wird - Ziel ist es, den Schaden zu verringern, wenn Entwickler versehentlich
math/randan Stellen einsetzen, an denencrypto/randerforderlich wäre - Go-APIs für Zufallszahlen waren traditionell in zwei Kategorien aufgeteilt
math/rand: statistische Zufallszahlen für Simulationen, Sampling, numerische Analyse, nicht-kryptografische randomisierte Algorithmen, Fuzzing, Shuffling, exponentielles Backoff usw.crypto/rand: kryptografische Zufallszahlen für Schlüssel, Tokens und andere Fälle, die Unvorhersagbarkeit erfordern
Warum statistischer Zufall nicht ausreicht
- Statistische Zufallszahlengeneratoren können für viele nicht-kryptografische Zwecke genügen, wenn sie grundlegende statistische Tests bestehen
- Wenn ein Beobachter jedoch den Algorithmus kennt und genügend Ausgaben sieht, kann er in vielen Fällen die folgende Sequenz vorhersagen
srandundrandaus Unix V3 waren frühe Formen, die später die Zufalls-APIs von C und vielen anderen Sprachen beeinflussten- Sie setzen den Zustand mit einem einzelnen Integer-Seed
- Der nächste Wert wird mit einem Linear Congruential Generator (LCG) berechnet
- Der interne Zustand ist so einfach, dass sich zukünftige Werte schon aus einer einzigen Ausgabe leicht berechnen lassen
- Bei einem LCG lassen sich Konstanten so wählen, dass alle möglichen Ausgabewerte vor einer Wiederholung einmal ausgegeben werden, allerdings hat er die Schwäche, dass sich die niedrigen Bits in kurzen Zyklen wiederholen
Struktur und Schwächen des Go-1-Generators
- Der
math/rand-Generator von Go 1 gehört zur Familie der Linear Feedback Shift Register - Der interne Zustand ist ein
vec-Slice aus 607uint64vec[606]ist der „tap“vec[334]ist der „feed“- Beim Erzeugen des nächsten Werts werden tap und feed addiert, um
xzu bilden;xwird an der feed-Position gespeichert und dann zurückgegeben
- Die tatsächliche Implementierung verschiebt nicht das gesamte Slice, sondern bewegt nur die tap- und feed-Positionen rückwärts, um Kosten zu reduzieren
- Für die Erzeugung des nächsten Werts sind zwei Subtraktionen, zwei bedingte Additionen, zwei Loads, eine Addition und ein Store nötig
- Da der Rückgabewert ein Element des internen Zustandsvektors ist, wird nach dem Lesen von 607 Ausgaben der gesamte Zustand offengelegt
- Wenn man dasselbe
vecfüllt und den Algorithmus ausführt, lassen sich zukünftige Werte vorhersagen - Führt man den Algorithmus rückwärts aus, lassen sich auch vergangene Werte rekonstruieren
- Wenn man dasselbe
- Der Go-1-Generator ist nicht für Sicherheitszwecke gedacht, und die Qualität der erzeugten Zahlen hängt auch von der initialen
vec-Konfiguration ab
Was PCG verbessert und welche Grenzen bleiben
math/rand/v2übernimmt PCG von Melissa O’Neill als moderneren statistischen Zufallszahlengenerator- Go-PCG basiert auf einem 128-Bit-LCG und reduziert den 128-Bit-Zustand über eine
scramble-Funktion auf eine 64-Bit-Ausgabe - In Go wird auf Basis von O’Neills Vorschlag aus der Proposal-Diskussion ein multiplikationsbasiertes
scrambleverwendet- Diese Form heißt PCG-DXSM
- Auch Numpy verwendet diese PCG-Form
- PCG hat einen deutlich kleineren Zustand als der Go-1-Generator
- Go-1-Generator: 607
uint64 - PCG: zwei
uint64
- Go-1-Generator: 607
- PCG ist weniger empfindlich gegenüber Initialzuständen und besteht mehrere statistische Tests, garantiert aber keine Unvorhersagbarkeit
- PCG-XSL-RR ist umkehrbar
- Auch bei PCG-DXSM wäre es nicht überraschend, wenn er umkehrbar ist
- Für die Erzeugung von Geheimwerten ist nicht PCG, sondern ein anderer Generator nötig
Kryptografische Zufallszahlen und die Rolle des Betriebssystems
- Kryptografische Zufallszahlen müssen praktisch unvorhersagbar bleiben, selbst für Beobachter, die die Erzeugungsmethode kennen und viele frühere Ausgaben gesehen haben
- Kryptografische Protokolle, geheime Schlüssel, moderner Handel und Online-Privatsphäre hängen von kryptografischen Zufallszahlen ab
- Die tatsächliche Zufallsversorgung übernimmt das Betriebssystem
- Es sammelt Zufälligkeit aus physischen Geräten wie Maus, Tastatur, Festplatte und Netzwerk-Timing
- In jüngerer Zeit wird auch elektrisches Rauschen genutzt, das die CPU direkt misst
- Sobald das Betriebssystem genügend Zufall gesammelt hat, etwa mindestens 256 Bit, erzeugt es mit Hash- oder Verschlüsselungsalgorithmen lange Zufallssequenzen
- Früher wurden hauptsächlich Device-Dateien wie
/dev/randomverwendet, heute bieten Betriebssysteme direkte Systemaufrufe an - Go versteckt die Unterschiede zwischen Betriebssystemen in
crypto/randund stellt mitrand.Readeine einheitliche Schnittstelle bereit
Design von ChaCha8Rand
- Der neue Generator ChaCha8Rand in Go 1.22 ist eine leicht modifizierte Form von Daniel J. Bernsteins ChaCha-Stream-Chiffre
- ChaCha ist weit verbreitet, insbesondere in der Form ChaCha20, die auch in TLS und SSH verwendet wird
- Jean-Philippe Aumassons Too Much Crypto betrachtet auch die 8-Runden-Variante ChaCha8 als sicher; ChaCha8 ist ungefähr 2,5-mal schneller
- ChaCha8Rand nutzt ChaCha8 als
rand.Source, indem die erzeugten Blöcke nicht mit der Eingabe XOR-verknüpft, sondern direkt als Zufallsstrom verwendet werden- Das entspricht dem Ver- oder Entschlüsseln von Daten, die nur aus Nullen bestehen
Änderungen in ChaCha8Rand
- ChaCha8Rand verwendet einen 32-Byte-Seed als ChaCha8-Schlüssel
- ChaCha8 erzeugt 64-Byte-Blöcke; die Berechnung behandelt den Block als 16
uint32 - Normale Implementierungen können mit SIMD-Instruktionen 4 Blöcke gleichzeitig berechnen, müssen die interleaved Blöcke aber wieder entflechten, um sie für XOR-Eingaben zu verwenden
- ChaCha8Rand definiert diese interleaved Blöcke selbst als Zufallsstrom und eliminiert dadurch die Unshuffle-Kosten
- In der Abschlussphase eines ChaCha8-Blocks werden bestimmte Werte zu jedem
uint32addiert- Die eine Hälfte ist Schlüsselmaterial, die andere Hälfte bekannte Konstanten
- ChaCha8Rand addiert die bekannten Konstanten nicht erneut und entfernt so die Hälfte der finalen Additionen
- Bei jedem 16. erzeugten Block werden die letzten 32 Byte als Schlüssel für die nächsten 16 Blöcke verwendet
- Dieses Rekeying bietet eine Art Forward Secrecy
- Selbst wenn der gesamte Speicherzustand des Generators offengelegt wird, lassen sich nur Werte seit dem letzten Rekeying rekonstruieren; auf frühere Werte gibt es keinen Zugriff
- Go hat die ChaCha8Rand-C2SP-Spezifikation und Testfälle veröffentlicht, damit andere Implementierungen bei gleichem Seed dieselbe Wiederholbarkeit wie die Go-Implementierung teilen können
Einsatzorte in der Standardbibliothek
- Die Go-Runtime hält einen ChaCha8Rand-Zustand pro Core, der mit vom Betriebssystem gelieferten kryptografischen Zufallszahlen geseedet wird
- Die Zustandsgröße pro Core beträgt 300 Byte
- Auf einem 16-Core-System liegt das ungefähr auf dem Niveau des einzelnen gemeinsam genutzten Go-1-Generatorzustands mit 4.872 Byte
- Dank des Zustands pro Core lassen sich ohne Lock-Contention schnell Zufallszahlen erzeugen
- Paketfunktionen von
math/rand/v2verwenden immer ChaCha8Rand- Beispiele:
rand.N,rand.Float64
- Beispiele:
- Paketfunktionen von
math/randverwenden ChaCha8Rand, wennrand.Seednicht aufgerufen wurde- Beispiele:
rand.Intn,rand.Float64 - Wird
rand.Seedaufgerufen, muss aus Kompatibilitätsgründen auf den Go-1-Generator zurückgefallen werden
- Beispiele:
- Die Runtime wählt den Hash-Seed neuer Maps mit ChaCha8Rand statt mit dem bisherigen wyrand-basierten Generator
- Wenn ein Angreifer die konkrete Hash-Funktion der Map-Implementierung kennt, kann er Eingaben vorbereiten und die Map zu quadratischer Laufzeit zwingen
- Ein Seed pro Map statt eines globalen Seeds kann auch andere degenerierte Verhaltensweisen vermeiden
- Ob kryptografische Zufallszahlen für Map-Seeds zwingend nötig sind, ist nicht klar, aber die Umstellung war einfach und eine vorsichtige Wahl
- Code, der eine separate ChaCha8Rand-Instanz benötigt, kann direkt
rand.ChaCha8erzeugen
Begrenzung des Schadens durch Sicherheitsfehler
- Go will dabei helfen, standardmäßig sicheren Code zu schreiben, indem häufige Fehler mit Sicherheitsfolgen reduziert oder beseitigt werden
- Als
Readausmath/randin Go 1.20 deprecated wurde, stellten einige Entwickler fest, dass siemath/randan Stellen verwendeten, an denencrypto/randnötig war, etwa zur Erzeugung von Schlüsselmaterial - In Go 1.20 war ein solcher Fehler ein ernstes Sicherheitsproblem
- Es musste untersucht werden, wofür der Schlüssel verwendet wurde
- wie der Schlüssel offengelegt wurde
- ob andere Zufallsausgaben einem Angreifer Hinweise zur Ableitung des Schlüssels gegeben hatten
- In Go 1.22 ist derselbe Fehler weiterhin ein Fehler, führt aber mit geringerer Wahrscheinlichkeit zu einer Sicherheitskatastrophe
- Trotzdem ist es für Geheimwerte besser,
crypto/randzu verwenden- Der Betriebssystemkernel kann Zufallswerte besser schützen
- Der Kernel fügt dem Generator kontinuierlich neue Entropie hinzu
- Kernel-Implementierungen wurden stärker geprüft
Fälle, die nicht nach Kryptografie aussehen
- Für die Erzeugung zufälliger UUIDs könnte
math/randausreichend erscheinen, weil UUIDs keine Geheimwerte sind - Wird
math/randaber mit der aktuellen Zeit geseedet, können verschiedene Computer, die im selben Moment laufen, dieselben Werte erzeugen- Auf Systemen, bei denen die aktuelle Zeit nur Millisekunden-Präzision bietet, steigt diese Wahrscheinlichkeit
- Selbst mit dem OS-Entropie-basierten Auto-Seeding aus Go 1.20 ist der Seed des Go-1-Generators nur ein 63-Bit-Integer
- Ein Programm, das beim Start eine UUID erzeugt, ist auf 2⁶³ mögliche erste UUIDs beschränkt
- Nach etwa 2³¹ UUIDs entsteht eine Kollisionswahrscheinlichkeit
- ChaCha8Rand in Go 1.22 wird mit 256 Bit Entropie geseedet
- Es gibt 2²⁵⁶ mögliche erste UUIDs
- Um Kollisionen muss man sich keine Sorgen machen
- Auch Load Balancing, bei dem ein Frontend-Server Anfragen zufällig Backend-Servern zuweist, kann unvorhersagbare Zufallszahlen benötigen
- Wenn ein Angreifer die Zuweisung beobachtet und den vorhersagbaren Algorithmus kennt, kann er teure Anfragen auf ein bestimmtes Backend konzentrieren
- Beim Go-1-Generator ist das selten, aber möglich
- In Go 1.22 ist es kein Problem
Performance-Eigenschaften
- Die Sicherheitsvorteile von ChaCha8Rand haben geringe Kosten, die Performance liegt aber in derselben Größenordnung wie beim Go-1-Generator und bei PCG
- Verglichen werden zwei Operationen
Uint64: gibt das nächsteuint64aus dem Zufallsstrom zurückN(1000): gibt eine Zufallszahl im Bereich[0, 1000)zurück
- Wird auf einem 64-Bit-x86-Chip mit
GOARCH=386gebaut und im 32-Bit-Modus ausgeführt, ist PCG wegen der 128-Bit-Multiplikation langsamer als ChaCha8Rand- ChaCha8Rand nutzt 32-Bit-SIMD-Arithmetik
- Auf einigen Systemen ist
Go 1: Uint64schneller alsPCG: Uint64, aberGo 1: N(1000)ist langsamer alsPCG: N(1000)N(1000)in Go 1 verwendet zwei 64-Bit-Integer-Divisionen zur BereichsreduktionN(1000)in PCG und ChaCha8 nutzt den schnellerenmath/rand/v2-Algorithmus, der Divisionen meist vermeidet
- Insgesamt ist ChaCha8Rand langsamer als der Go-1-Generator, aber nicht mehr als doppelt so langsam
- Auf typischen Servern überschreitet der Unterschied 3 ns nicht; nur sehr wenige Programme werden dadurch einen Bottleneck haben
Fazit
- Go 1.22 erhöht die Sicherheit von Programmen ohne Codeänderungen
- Der zentrale Ansatz besteht darin,
math/randselbst zu stärken, um das häufige Problem zu verringern, dass versehentlichmath/randstattcrypto/randverwendet wird - Es gibt auch Fälle wie das npm-Paket
keypair, das bei fehlender Web Crypto API versucht, RSA-Schlüsselpaare mit JavaScriptMath.randomzu erzeugen - Systemsicherheit kann nicht davon abhängen, dass Entwickler keine Fehler machen
- ChaCha8Rand in Go 1.22 zeigt, dass der Ansatz, auch für „mathematische“ Zufallszahlen einen kryptografisch starken Pseudozufallszahlengenerator zu verwenden, eine mit anderen Generatoren konkurrenzfähige Performance erreichen kann
1 Kommentare
Hacker-News-Meinungen
Wie im Artikel beschrieben, ist uns bei rclone genau dieser Fehler passiert.
Beim Refactoring von Code, der
Readauscrypto/randverwendete, wurde der Import automatisch geändert; vermutlich wurde er mit Code vermischt, dermath/randnutzte, undgoimportshat ihn dann aufmath/randumgestellt.Dadurch verwendeten wir keinen kryptografisch sicheren Zufallszahlengenerator, sondern einen deterministischen Generator, den rclone mit der Uhrzeit seedete, und im Diff ist es nicht aufgefallen :-(
https://www.cvedetails.com/cve/CVE-2020-28924/
Deshalb begrüße ich diese Änderung sehr.
goimportsso geändert, dass escrypto/randbevorzugt, daher ist nicht ganz klar, was beim Refactoring passiert ist.Vielleicht kam in dieselbe Datei Code, der APIs speziell aus
math/randverwendete.https://go-review.googlesource.com/24847
Jedenfalls ist es gut, dass solche Dinge jetzt aufgeräumt werden.
math/randwerde verwendet. Tatsächlich war das nicht der Fall, es wurden nur mehrere Dateien verwechselt; also keine große Sache, aber es zeigt, wie verwirrend das Ganze ist.Bei
text/templateundhtml/templateist es ähnlich. Im Rückblick war dieses Verdecken von Paketnamen eine schlechte Idee."secure password generation golang"sucht, sieht man, dass fast alle Beispielemath/randverwenden.Noch schlimmer: Sie initialisieren den Seed alle direkt vor dem Erzeugen des Passworts mit der aktuellen Uhrzeit.
Das habe ich herausgefunden, nachdem ich in unserem Code entdeckt hatte, dass jemand
math/randnutzte, und wissen wollte, woher das kopiert worden war.goimportshatmath/rand.Readundcrypto/rand.Readpraktisch von Anfang an speziell behandelt.Allerdings erwähnt der Commit von 2016 unter https://github.com/golang/tools/commit/0835c735343e0d8e375f0... eine Zeit, in der
"rand.Read"als"math/rand"interpretiert werden konnte.Vielleicht fiel es genau in diese Phase.
"PredictableRand"bereitzustellen.Auch letzte Woche hatte spacey das schon unter https://news.ycombinator.com/item?id=40237491 gepostet, aber dieser Beitrag scheint fälschlicherweise als Duplikat von https://news.ycombinator.com/item?id=40224864 untergegangen zu sein.
Die beiden Blogposts auf go.dev sind zwei Teile derselben Reihe, unterscheiden sich aber deutlich. Dieser Beitrag handelt von effizienten Algorithmen für kryptografisch sichere Zufallszahlen, der frühere von Go-API-Design.
Russell Cox liefert kontinuierlich hervorragende technische Blogposts, Vorschläge und Arbeitsergebnisse.
Wer seine Klarheit beim Schreiben und Denken verbessern möchte, hat mit Russell Cox einen guten Ausgangspunkt.
Damals wusste ich nicht einmal, wer Russ Cox war, aber diese Artikelreihe war wirklich großartig.
Sie ist wahrscheinlich das qualitativ beste frei verfügbare Material zur Implementierung regulärer Ausdrücke; danach kommen diverse compilerorientierte Bücher, die aber weder kostenlos noch im Web leicht auffindbar sind.
Ich habe einmal
math/randdort verwendet, wo unbedingtcrypto/randnötig gewesen wäre.In der Folge wurden in frühen Versionen von dnscrypt-proxy2 statische Schlüssel verwendet.
Ursache war eine VSCode-Erweiterung, die Imports automatisch hinzufügte. In allen Quelldateien, in denen sichere Zufallszahlen nötig waren, hatte ich
crypto/randbewusst und sorgfältig importiert; in einer Datei fehlte es aber, alles kompilierte und funktionierte, und ich bemerkte nicht, dass die Erweiterung in genau dieser Datei stillschweigend einenmath/rand-Import hinzugefügt hatte.Seitdem importiere ich
crypto/randmit dem Aliascryptorand, um zu vermeiden, dass das falscherandautomatisch importiert wird.Nebenbei: Auch Zig verwendet einen auf ChaCha8 basierenden Zufallszahlengenerator; bei kryptografischen Operationen können Nutzer keinen eigenen Generator bereitstellen, sondern es wird immer ein sicherer Generator verwendet. Für Tests nehmen einige Funktionen explizite Seeds entgegen.
Für eingeschränkte Umgebungen enthält die Standardbibliothek außerdem einen kleineren Generator auf Basis der Ascon-Permutation und der Reverie-Konstruktion.
2016 wurde
goimportsso geändert, dass escrypto/randgegenübermath/randbevorzugt (https://go-review.googlesource.com/24847), und das war, bevor es VSCode-Unterstützung für Go gab.Auch in den 2020er-Jahren habe ich mich oft gefragt, warum die Standardimplementierungen für Zufallszahlen in mehreren Programmiersprachen schnelle Zufallszahlengeneratoren wie LFSR oder MT verwenden.
Ich finde, man sollte konservativ davon ausgehen, dass die Leute nicht wissen, ob sie einen Pseudozufallszahlengenerator oder einen kryptografisch sicheren Pseudozufallszahlengenerator brauchen, den Standard auf Letzteren umstellen und nur diejenigen, die Ersteren benötigen, ihn explizit auswählen lassen.
Wenn Entwickler nicht explizit auswählen, welche Random-Engine sie verwenden wollen, bekommen sie einen kryptografisch sicheren Generator.
Der schwierige Teil ist nun, die Leute davon zu überzeugen, auf die neue API umzusteigen. Darüber hinaus ist es nicht einmal leicht, sie von
mt_rand(), das eine globaleMt19937-Instanz verwendet, auf das seit PHP 7.0 verfügbare CSPRNG-basierterandom_int()umzustellen.[1] https://www.php.net/releases/8.2/en.php#random_extension
In meinem Anwendungsfall gab es zehntausende Komponenten, und beim Profiling zeigte sich, dass ein erheblicher Teil der Initialisierungszeit der Datenstruktur in
Read()voncrypto/randfloss; auf meinem MacBook wurden dabei Systemaufrufe ausgeführt.Nachdem ich die Bibliothek gepatcht hatte, damit sie
Read()ausmath/randverwendet, wurde die Performance deutlich besser.Abgesehen davon, dass
math/randschneller ist, machte ich mir Sorgen, ohne guten Grund den Entropie-Pool des Systems zu erschöpfen. In diesem Fall war der einzige Grund, warum die IDs zufällig sein mussten, dass man die Datenstruktur serialisieren/deserialisieren und später weitere Komponenten hinzufügen könnte – was ich nicht vorhatte.Ich weiß nicht, wie genau der Zeitpunkt der in diesem Blog beschriebenen Änderung zu meiner Erfahrung passt. Vielleicht habe ich eine ältere Version der Bibliothek verwendet, und wenn
crypto/randheute praktisch kaum noch vonmath/randzu unterscheiden ist, dann umso besser :-)Die Zustandsgröße ist immer noch relativ groß (64 Byte gegenüber 16 Byte), aber deutlich besser als bei
mt19937oder dem alten Go-PRNG.Wenn ein CSPRNG viel langsamer ist – wie es bei normalen CSPRNGs, die keine ChaCha-Variante mit reduzierter Rundenzahl sind, meist der Fall ist –, verliert er als Standardwert an Attraktivität.
Es gibt noch einen kleinen Faktor, der Leute in Richtung PRNG drängt, obwohl sie keinen Seed brauchen: CSPRNG-APIs enthalten Fehler, die man immer behandeln muss, für den Fall von Systemaufruffehlern oder mangelnder Entropie.
Wie oft schlägt ein Lesen aus
crypto/randtatsächlich fehl? Wie viel müsste man auf modernen Systemen lesen, um die Entropie zu erschöpfen? Ich habe selbst bei Milliarden von Anfragen keinen Fehler gesehen, und auchddfunktioniert problemlos.Ich frage mich, ob für die meisten Anwendungsfälle eine API im Stil von
Must/panicals Standard passender wäre.Nebenbei habe ich mir Pythons Paket
secrets(https://docs.python.org/3/library/secrets.html) angesehen, und dort findet sich keinerlei Hinweis darauf, dass Exceptions geworfen werden können. Passiert das in der Praxis einfach nicht?Man verliert etwas Performance, erhält dafür aber eine deutlich stärkere Garantie, keine Katastrophe durch die Verwendung des falschen Zufallszahlengenerators auszulösen.
Schade, dass Entwickler in fast allen Sprachen immer noch auf diese scharfe Kante achten müssen.
Für alle, die es nicht wissen:
gosecund dessen Erweiterunggolangci-lintwarnen vor der Verwendung vonmath/rand.https://github.com/securego/gosec/blob/d3b2359ae29fe344f4df5...
math/rand/v2am besten gefallen, ist, dass man es im Unternehmen ohnenolint-Direktiven und die anschließende PR-Diskussion verwenden kann.Ich bin noch dabei, die Empfehlungen zu Sicherheit und den neuen v2-Optionen einzuordnen.
Der Blogpost verwendet Formulierungen wie „für geheime Werte braucht man etwas anderes“ und behandelt anschließend ausführlich kryptografische Zufälligkeit, ChaCha8 und die Seed-Erzeugung aus Systemzufall, sodass es einen sehr „sicheren“ Eindruck macht.
In der Paketdokumentation steht jedoch:
... but it should not be used for security-sensitive work ... This package's outputs might be easily predictable regardless of how it's seeded. For random numbers suitable for security-sensitive work, see the crypto/rand package.Warum erweckt der Blogpost dann den Eindruck, als würde
math/rand/v2für „geheime Werte“ verwendet?Kurz gesagt: Bedeutet das, dass für alles Sensible weiterhin
crypto/randgenutzt werden sollte und die hier beschriebenen Verbesserungen ein Sicherheitsnetz sind, falls jemandmath/rand/v2unangemessen verwendet?math/rand/v2ist nicht optimal, aber wenn man es versehentlich dort verwendet, wo mancrypto/randhätte verwenden sollen, ist das nicht mehr sofort eine fatale Sicherheitslücke.Im Text steht auch:
Es ist weiterhin besser,
crypto/randzu verwenden. Der Betriebssystemkernel ist besser darin, Zufallswerte vor verschiedenen Ausspähangriffen geheim zu halten, er fügt dem Generator laufend neue Entropie hinzu und wurde umfassender geprüft. Aber die versehentliche Verwendung vonmath/randist nicht mehr automatisch eine Sicherheitskatastrophe.Selbst in den schlechtesten Benchmarks ist die neue Strategie nur ungefähr halb so langsam wie ein unsicherer Zufallszahlengenerator, und die meisten Benchmarks lagen deutlich näher beieinander.
Go trifft für die Standardbibliothek und die darauf aufbauenden Apps eine gute Balance zwischen Sicherheit und Performance. Es wäre wünschenswert, wenn andere Ökosysteme nachziehen würden.
Wenn eine Anwendung schnelle, unsichere Zufallszahlen braucht, muss sie einen internen Generator selbst implementieren.
Unsichere Zufallszahlen in Griffweite zu platzieren, ist ein Fußschuss-Werkzeug, das man wegräumen kann.
Leute dazu zu ermutigen, anzunehmen, dass eine
"random"-Primitive kryptografisch sicher ist, fördert schlechte Praktiken.math/rand/v2kryptografisch sicher zu machen, kann zwar ein Problem lösen, aber nun wird etwas, das nicht nach einem Sicherheitsversprechen aussieht, als „okay“ angesehen.Im Allgemeinen gibt es bei
math/rand-Funktionen keine Konvention, dass sie kryptografisch sicher sind. Wenn man das ändert und schlechten Code zufällig korrekt funktionieren lässt, kann das verschleiern, welche anderen Fehler wir machen, wenn wir schon solche offensichtlichen Fehler begehen.Go 1s
math/randbezeichnet man genauer als additiven verzögerten Fibonacci-Generator.Die erste Veröffentlichung dazu war ein Paper von Green, Smith und Klem.
[1] https://doi.org/10.1145/320998.321006
https://www.leviathansecurity.com/blog/attacking-gos-lagged-... kenne ich ebenfalls, und auch dort wird er verzögerter Fibonacci-Generator genannt.
Rob Pike und ich haben vor einigen Monaten per E-Mail mit Don Mitchell korrespondiert, der die ursprüngliche C-Version des Go-1-Generators geschrieben hat, und ihn gefragt, wie er diesen Algorithmus beschreiben würde. Er antwortete: „Soweit ich mich erinnere, haben Jim und ich einen Marsaglia-LFSR-ähnlichen Generator implementiert.“
Beide Beschreibungen — verzögerter Fibonacci und LFSR-ähnlicher Generator — sind aus unterschiedlichen Blickwinkeln meines Erachtens korrekt. Beides ist in Ordnung, aber im Text habe ich mich entschieden, die Beschreibung des ursprünglichen Autors zu verwenden.
Wenn man einen kleinen Makel nennen will: Hier scheinen statistische Zufälligkeit und Pseudozufallszahlengenerator vermischt zu werden.
Die Wikipedia-Definition von statistischer Zufälligkeit lautet: „Eine Zahlenfolge heißt statistisch zufällig, wenn sie keine erkennbaren Muster oder Regelmäßigkeiten aufweist.“
Gilt diese Definition auch für echte Zufallszahlengeneratoren (TRNGs)? Das sollte man hoffen. Zumindest langfristig oder im Grenzfall sollte es so sein. Andernfalls wäre es kein TRNG.
Ein TRNG muss langfristig eine „Zahlenfolge ohne erkennbare Muster oder Regelmäßigkeiten“ erzeugen.
Daher bedeutet statistische Zufälligkeit nicht PRNG, aber man kann sagen, dass sie auch auf TRNGs anwendbar ist.
Das Problem scheint daher zu kommen, dass es viele Tests auf statistische Zufälligkeit gibt, mit denen geprüft wird, ob ein PRNG eine eingeschränkte Form statistischer Zufälligkeit besitzt.
Um einen PRNG zu bezeichnen, wäre daher wohl der Ausdruck „Pseudozufallszahlengenerator“ passender gewesen als „statistische Zufälligkeit“. Aber das ist nur ein sehr kleiner Makel.