- In Go 1.22 wurde
math/rand sowie das neu eingeführte Paket math/rand/v2 so geändert, dass sie einen kryptografisch sicheren Zufallszahlengenerator verwenden. Dadurch wird eine bessere Zufälligkeit erreicht, und der Schaden wird deutlich verringert, wenn Entwickler versehentlich math/rand statt crypto/rand verwenden.
Unterschied zwischen statistischer und kryptografischer Zufälligkeit
- Statistische Zufälligkeit eignet sich für Simulationen, Sampling, numerische Analyse, nicht-kryptografische Zufallsalgorithmen, Zufallstests, das Mischen von Eingaben, zufälliges exponentielles Backoff usw.
- Selbst sehr einfache und leicht berechenbare mathematische Formeln funktionieren für solche Einsatzzwecke ausreichend gut. Ein Beobachter, der den verwendeten Algorithmus kennt, kann jedoch nach einer gewissen Anzahl beobachteter Werte die folgende Sequenz vorhersagen.
- Kryptografische Zufälligkeit muss praktisch vollständig unvorhersagbar sein, selbst wenn zuvor erzeugte Werte beobachtet wurden.
- Sichere kryptografische Protokolle, geheime Schlüssel, moderner Handel und Online-Privatsphäre hängen stark von kryptografischer Zufälligkeit ab.
Der math/rand-Generator in Go 1
- Verwendet ein Linear-feedback shift register (LFSR).
- Problematisch ist, dass der interne Zustand vollständig als Vektor aus 607
uint64 offengelegt werden kann.
- Liest man 607 Werte aus dem Generator, ist der gesamte Zustand offengelegt und spätere Werte lassen sich vorhersagen.
Der PCG-Generator in math/rand/v2
- Verwendet Melissa O'Neills PCG-Algorithmus. Dabei handelt es sich um ein 128-Bit-LCG mit Nachbearbeitung.
- Der gesamte Zustand besteht aus einer einzelnen 128-Bit-Zahl, die Aktualisierung erfolgt durch 128-Bit-Multiplikation und Addition.
- In Go wird nach O'Neills Vorschlag statt einer XOR-basierten eine multiplikationsbasierte Scramble-Funktion verwendet, um die Bits aggressiver zu mischen.
- Der Rechenaufwand ist höher als beim Go-1-Generator, dafür wird deutlich weniger Speicher für den Zustandswert benötigt, der Generator ist weniger empfindlich gegenüber dem initialen Zustand und besteht auch statistische Tests, an denen andere Generatoren scheitern.
- Dennoch ist auch PCG weiterhin nicht unvorhersagbar.
Kryptografische Zufälligkeit
- Letztlich muss das Betriebssystem echte Zufälligkeit aus dem Rauschen physischer Geräte sammeln.
- Sobald genügend Zufälligkeit gesammelt wurde (256 Bit oder mehr), lässt sie sich mit kryptografischen Hashes oder Verschlüsselungsalgorithmen auf Zufallsfolgen beliebiger Länge ausdehnen.
- Das Paket
crypto/rand in Go abstrahiert diese Unterschiede der Betriebssystem-Schnittstellen und bietet mit rand.Read dieselbe Schnittstelle.
Der Generator ChaCha8Rand
- Ein neuer Generator, der durch Modifikation von DJBs ChaCha-Stream-Cipher entstanden ist.
- Verwendet ChaCha8, also die 8-Runden-Version. Sie ist 2,5-mal schneller als ChaCha20 und dennoch sicher.
- Verwendet einen 32-Byte-Seed als ChaCha8-Schlüssel. Alle 16 Blöcke werden die letzten 32 Byte der erzeugten Blöcke als Schlüssel für die nächsten 16 Blöcke verwendet, was Forward Secrecy bietet.
rand.Float64, rand.N usw. in math/rand/v2 verwenden immer diesen Generator.
- Auch
math/rand verwendet diesen Generator. Wird jedoch rand.Seed aufgerufen, kommt der Go-1-Generator zum Einsatz.
- Auch die Runtime verwendet ChaCha8Rand bei der Auswahl des Hash-Seeds für neue Maps.
Behebung von Sicherheitsfehlern
- Go 1.22 macht Programme durch die Härtung von
math/rand sicherer, ohne dass Code geändert werden muss.
- Wenn zum Beispiel
math/rand.Read fälschlich für die Schlüsselerzeugung verwendet wurde, war das in Go 1.20 ein schwerwiegendes Sicherheitsproblem, in Go 1.22 ist es nur noch ein Fehler.
- Auch bei Einsatzzwecken, die nicht offensichtlich nach „Krypto“ aussehen, etwa UUID-Erzeugung oder Lastverteilung von Frontend-Servern, ist ChaCha8Rand deutlich robuster als der Go-1-Generator.
Leistung
- ChaCha8Rand zeigt eine ähnliche Performance wie der Go-1-Generator oder PCG.
- Auf 32-Bit-Code ist ChaCha8Rand schneller als PCG, das 128-Bit-Multiplikation benötigt.
- Dank der Algorithmen in
math/rand/v2, die 64-Bit-Division vermeiden, können ChaCha8Rand oder PCG bei N(1000)-Operationen sogar schneller sein als der Go-1-Generator.
- Insgesamt ist ChaCha8Rand langsamer als der Go-1-Generator, aber nie mehr als doppelt so langsam; auf typischen Servern beträgt der Unterschied nicht mehr als 3 ns.
Meinung von GN⁺
- Der Einsatz von ChaCha8Rand in Go 1.22 ist ein beispielhafter Eingriff auf Sprachebene, der die Sicherheit deutlich erhöht und den Performance-Verlust minimal hält. Besonders beeindruckend ist, dass häufige Fehler von Entwicklern auf Sprachebene von vornherein abgefangen werden.
- Wie auch im Haupttext erwähnt, sind solche Fehler nicht auf Go beschränkt, sondern kommen auch in anderen Sprachen häufig vor. Die Systemsicherheit sollte nicht von Entwicklerfehlern abhängen, deshalb sollten sich auch andere Sprachen wie Go in Richtung kryptografisch starker Pseudozufallszahlengeneratoren selbst für „mathematische“ Zufallszahlen bewegen.
- Allerdings ist ChaCha8Rand ungeeignet für kryptografische Primitive wie
crypto_box oder xchacha20poly1305. Für solche Einsatzzwecke sollte weiterhin direkt crypto/rand verwendet werden.
- Dass die Go-Runtime nun auch bei der Auswahl von Map-Hash-Seeds ChaCha8Rand verwendet, war etwas überraschend. Ob kryptografische Zufallszahlen für Hash-Seeds zwingend notwendig sind, ist nicht ganz klar, aber das zeigt das Sicherheitsbewusstsein des Entwicklungsteams, potenzielle unangenehme Angriffe von vornherein auszuschließen.
- Da die Qualität von
math/rand als Standardpaket auf Sprachebene gestiegen ist, dürfte es künftig auch mehr direkte Einsätze von math/rand in Anwendungen geben. Projekte, die bisher wegen der Vorhersagbarkeit von math/rand auf separate Zufallsbibliotheken gesetzt haben, könnten von dieser Änderung profitieren.
1 Kommentare
Hacker-News-Kommentar
Zusammengefasst ergibt sich Folgendes:
Read-Funktion des Paketsmath/randals deprecated markiert, wodurch Fälle entdeckt wurden, in denen sie fälschlich anstelle voncrypto/randverwendet wurde. Das führte zu dem Fehler, einen sicherheitsschwachen deterministischen Zufallszahlengenerator zu benutzen.gosecodergolangci-lintgeben Warnungen bei der Verwendung vonmath/randaus.math/rand/v2verwendet die ChaCha8-Chiffre und wird mit Systementropie geseedet, vermittelt also den Eindruck, „sicher“ zu sein, ist aber für sicherheitskritische Aufgaben weiterhin ungeeignet. Dafür solltecrypto/randverwendet werden.math/randin Go 1 kann genauer als ein additiver lagged-Fibonacci-Generator betrachtet werden.math/randerreicht selbst im schlechtesten Fall etwa die halbe Geschwindigkeit des bisherigen unsicheren Zufallszahlengenerators; in den meisten Benchmarks gab es nahezu keinen Unterschied. Go findet in der Standardbibliothek ein angemessenes Gleichgewicht zwischen Sicherheit und Leistung.java.util.Randomverhindert.