Sichere Zufallszahlengenerierung: Grundlage starker Kryptografie
Sichere Zufallsgenerierung ist entscheidend für Verschlüsselung. Wie crypto.getRandomValues funktioniert und warum schwache Zufälligkeit Sicherheit bricht.
Sichere Zufallszahlengenerierung ist das Fundament, auf dem jede andere kryptografische Primitive aufbaut. In JavaScript füllt crypto.getRandomValues(buffer) ein TypedArray mit kryptografisch sicheren Zufallsbytes aus dem CSPRNG des Betriebssystems (/dev/urandom unter Linux/macOS, BCryptGenRandom unter Windows, SecRandomCopyBytes unter iOS/macOS). Verwenden Sie Math.random() niemals für sicherheitsrelevante Zwecke — es handelt sich um einen Mulberry32- oder xorshift-artigen PRNG, der auf Geschwindigkeit ausgelegt ist, nicht auf Unvorhersagbarkeit, und seine Ausgabe ist nach wenigen beobachteten Werten vorhersagbar. Ein schwacher RNG bricht AES-Schlüssel, TLS-Handshakes, Nonce-Eindeutigkeit in GCM, Token-Unratbarkeit und jedes andere Sicherheitsprimitiv, das von unvorhersagbaren Bits abhängt.
Der Unterschied zwischen zufällig und kryptografisch zufällig
Ein PRNG (Pseudo-Zufallszahlengenerator) erzeugt einen deterministischen Strom aus einem Seed. Gegeben Seed und Algorithmus können Sie jede Ausgabe reproduzieren. Geeignet für Spiele, Simulationen und Monte-Carlo-Methoden. Katastrophal für Kryptografie.
Ein CSPRNG (kryptografisch sicherer PRNG) wird aus einer echten Entropiequelle (Wärmerauschen, Interrupt-Timing, Hardware-RNG-Instruktionen wie Intels RDSEED) gespeist, und sein Design stellt sicher, dass die Ausgabe rechnerisch von echter Zufälligkeit nicht unterscheidbar ist — und dass die Beobachtung vergangener Ausgaben keine Vorhersage künftiger Ausgaben ermöglicht.
JavaScript stellt Ihnen beides zur Verfügung. Math.random() ist ein PRNG. crypto.getRandomValues() ist ein Wrapper über den CSPRNG des Betriebssystems. Ein Zeile Code Unterschied, riesiger Sicherheitsunterschied.
Die kanonisch korrekte Verwendung
// 256-Bit-AES-Schlüssel als Zufallsbytes generieren
const keyBytes = crypto.getRandomValues(new Uint8Array(32));
// 96-Bit-GCM-Nonce generieren
const nonce = crypto.getRandomValues(new Uint8Array(12));
// 128-Bit-Salt generieren
const salt = crypto.getRandomValues(new Uint8Array(16));
// URL-sicheres zufälliges Token generieren
const tokenBytes = crypto.getRandomValues(new Uint8Array(32));
const token = btoa(String.fromCharCode(...tokenBytes))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
crypto.getRandomValues() ist synchron, füllt den Puffer in-place und gibt ihn zurück. Die maximale Anforderungsgröße beträgt 65.536 Bytes in einem einzelnen Aufruf (ein durch die Spezifikation aufgezwungenes Kontingent, um Blockierungen zu verhindern). Für größeres Zufallsmaterial rufen Sie die Funktion wiederholt auf.
Node.js-Äquivalente
const { randomBytes, randomFillSync, webcrypto } = require('crypto');
const keyBytes = randomBytes(32); // Gibt einen Buffer zurück
// Oder Web-Crypto-kompatibel
const nonce = webcrypto.getRandomValues(new Uint8Array(12));
Nodes randomBytes zieht aus demselben zugrunde liegenden CSPRNG wie Web Crypto. Verwenden Sie den API-Stil, der zu Ihrem Code passt. Für isomorphen Code, der in beiden Umgebungen läuft, stimmt webcrypto.getRandomValues exakt mit dem Browser überein.
Warum Math.random versagt
V8 (Chrome/Node), SpiderMonkey (Firefox) und JavaScriptCore (Safari) implementieren Math.random() alle als schnellen PRNG ohne kryptografische Garantien. V8 verwendet eine xorshift128+-Variante. Forscher haben gezeigt, dass ein Angreifer nach ~5 beobachteten Ausgaben den internen Zustand wiederherstellen und alle zukünftigen Ausgaben vorhersagen kann. Im Jahr 2015 rekonstruierten Mike Pound und Kollegen den V8-Math.random-Zustand in realen Bug-Bounties.
Wenn Sie Math.random() zur Generierung von Session-Tokens, Passwort-Reset-Links, Verschlüsselungs-Nonces oder Share-IDs verwenden, können Angreifer, die einige davon beobachten, den Rest vorhersagen. Das ist keine Theorie — es ist eine häufige Fehlerklasse in Audits.
Häufige Fehlanwendungen
Einen Bibliotheks-PRNG mit Math.random() seeden:
// FALSCH
const seed = Math.floor(Math.random() * 2**32);
Nichts Nachgelagertes kann zufälliger sein als der Seed. Verwenden Sie stattdessen crypto.getRandomValues(new Uint32Array(1))[0].
Date.now() als Entropie verwenden: Zeit ist in engen Fenstern ratbar. Selbst kombiniert mit einem kleinen Zufallsfaktor leaken Zeitstempel genug Bits für Angreifer.
Eigenes Mischen durch XOR-Verknüpfen von Quellen: Unterlassen Sie das. Betriebssystem-CSPRNGs mischen bereits jede nützliche Entropiequelle. Eigene Mischungen reduzieren die Entropie typischerweise, anstatt sie zu erhöhen.
Modulo-Bias bei der Bereichsgenerierung: randomBytes[0] % 10 ist nicht gleichmäßig über 0–9 verteilt, da 256 kein Vielfaches von 10 ist. Für gleichmäßig zufällige Ganzzahlen in einem Bereich verwenden Sie Rejection-Sampling:
function randomInt(max) {
const range = new Uint32Array(1);
const threshold = 2**32 - (2**32 % max);
do {
crypto.getRandomValues(range);
} while (range[0] >= threshold);
return range[0] % max;
}
Entropiequellen und Boot-Zeitprobleme
Unter Linux ist /dev/urandom nach dem frühen Bootvorgang immer sicher. In den ersten Sekunden des Bootvorgangs auf Systemen ohne Hardware-RNG kann der Kernel-Pool unzureichend gespeist sein. Dies wurde im Debian-OpenSSL-Bug von 2008 ausgenutzt, bei dem ein Patch das Entropy-Mischen entfernte und nur die Prozess-ID als Seed beließ. In diesem Zeitfenster generierte Schlüssel hatten nur 2^15 mögliche Werte — in Sekunden aufgelistet.
Moderne Systeme speisen den Kernel-CSPRNG aus: RDSEED auf x86-64 (wenn verfügbar), ARMv8.5-A-RNG-Instruktionen, Wärmerauschen von verschiedenen Peripheriegeräten, Interrupt-Timing, Tastatur/Maus bei interaktiver Nutzung. Auf Servern mit Hardware wie Intel Ice Lake oder AMD Zen 3+ wird der CSPRNG innerhalb von Mikrosekunden nach dem Boot gespeist.
Für Docker-Container: Das /dev/urandom des Hosts wird standardmäßig durchgereicht. Kein Handlungsbedarf. Für Serverless (AWS Lambda, Cloudflare Workers) übernimmt die Runtime das Entropy-Seeding pro Aufruf.
Session-Tokens und Share-IDs
Für einen Dateiübertragungsdienst generieren Sie zufällige Kennungen für:
- Datei-IDs in URLs (Angreifer sollten keine gültigen IDs erraten können)
- Share-Tokens für passwortgeschützte Links
- CSRF-Tokens
- Verschlüsselungsschlüssel (pro Datei AES-Schlüssel)
- Nonces für GCM
Mindestlänge: 128 Bit (16 Bytes) für Kollisions- und Unratbarkeitsschutz, 256 Bit (32 Bytes) für Schlüssel. URL-sicheres Kodieren via base64url fügt ~33% Länge hinzu; Hex verdoppelt die Länge.
Ein 32-Byte-base64url-kodiertes Token hat 43 Zeichen und ist bei 2^256 praktisch kollisionsfrei.
Auf schwache RNGs testen
Anzeichen, dass Ihr RNG defekt oder schwach ist:
- Identische Tokens, die von verschiedenen Anfragen generiert werden (Kollision in einem riesigen Raum)
- Ausgabe besteht visuelle Tests, scheitert aber an statistischen Batterien wie
dieharderoderPractRand - Seed-Wiederverwendung nach Prozessneustart — jeder Deploy verwendet denselben Anfangszustand
- Generierte Schlüssel fallen in Muster (z. B. erste 4 Bytes variieren, aber letzte 28 sind identisch)
In der Produktion sehen Sie das wahrscheinlich nicht, es sei denn, etwas ist katastrophal falsch. Der Fehlermodus ist in der Regel still: Angriffe werden einfach praktisch auf einem Suchraum, der eigentlich 2^256 groß sein sollte.
Audit: Jeder Aufruf von Math.random() in einer Codebase sollte überprüft werden. Ein grep nach Math.random über Ihren Quellbaum ist eine gute wöchentliche Hygieneprüfung. Das Konvertieren von sicherheitsrelevanten Aufrufstellen zu crypto.getRandomValues dauert Minuten und verhindert echte Schwachstellen.
Zufällige Strings und UUIDs
Für menschenlesbare Kennungen gibt crypto.randomUUID() eine v4-UUID (122 Bit Zufälligkeit) in einem Standardformat zurück:
const id = crypto.randomUUID();
// "f47ac10b-58cc-4372-a567-0e02b2c3d479"
Unterstützt in Chrome 92+, Firefox 95+, Safari 15.4+, Node 14.17+. Gut für Datenbank-Primärschlüssel, API-Request-IDs und nicht sicherheitskritische eindeutige Bezeichner. Verwenden Sie explizites getRandomValues für alles, das benutzerdefinierte Formate oder höhere Entropie erfordert.
Auf Servern: Eigene RNG-Pools vermeiden
Einige Server-Frameworks bieten ihre eigenen zufälligen Pools an, die behaupten, den System-CSPRNG mit anwendungsseitiger Entropie zu „mischen". Behandeln Sie das mit Skepsis. Benutzerdefiniertes Mischen verbessert selten die Ausgabe des Kernels und kann die Entropie bei einem Bug stillschweigend verringern.
Wenn Sie Node oder eine große Runtime verwenden, ist das eingebaute crypto.randomBytes korrekt und schnell. Ersetzen Sie es nicht durch Drittanbieter-Mixer.
Das Fazit
Jedes kryptografische Element in einer Dateiübertragungsapp hängt von unvorhersagbaren zufälligen Bytes ab. AES-Schlüssel, GCM-Nonces, PBKDF2-Salts, Share-Tokens, Anti-CSRF-Tokens, Session-IDs — alle benötigen dasselbe Primitiv: crypto.getRandomValues() in Browsern, crypto.randomBytes() oder webcrypto.getRandomValues in Node. Verwenden Sie diese. Niemals Math.random(). Niemals Zeitstempel. Niemals einen eigenen Mixer.
HexaTransfer leitet jeden dateigebundenen AES-Schlüssel, jede Nonce und jeden URL-Bezeichner aus crypto.getRandomValues() auf dem Client ab. Serverseitige Share-IDs kommen aus crypto.randomBytes. Eine API, konsistentes Verhalten, keine Möglichkeit, versehentlich vorhersagbare Bits in das System einzuschleusen.
Das Primitiv ist genau deswegen unspektakulär, weil es das sein muss. Unspektakulär, korrekt und überall verfügbar — genau das, was Kryptofundamente sein sollten.
Auf hexatransfer.com testen — kostenlos, ohne Konto, bis 10 GB.
Große Dateien sicher mit Ende-zu-Ende-Verschlüsselung senden
Übertragen Sie Dateien bis zu 10 GB kostenlos mit Ende-zu-Ende-Verschlüsselung. Kein Konto erforderlich. Ihre Dateien werden in Ihrem Browser verschlüsselt, bevor sie hochgeladen werden — niemand sonst kann sie lesen.
Datei senden