Unzureichende Entropie in PRNG
Beschreibung
Unzureichende Entropie in PRNG ist eine Schwachstelle, die auftritt, wenn einem Pseudo-Zufallszahlengenerator (PRNG) beim Seeding oder Betrieb ausreichende Entropie fehlt, was sowohl Stabilitäts- als auch Sicherheitsbedrohungen erzeugt. PRNGs benötigen Entropie aus externen Quellen, um unvorhersagbare Ausgaben zu erzeugen - ohne ausreichende Entropie wird die PRNG-Ausgabe vorhersagbar. Diese Schwäche kann sich auf zwei Arten manifestieren: Wenn der PRNG bei Entropieerschöpfung geschlossen ausfällt, kann die Anwendung hängen bleiben oder abstürzen (Verfügbarkeitsauswirkung); wenn er offen ausfällt, indem er Ausgaben ohne ausreichende Entropie erzeugt, werden die generierten Werte vorhersagbar (Sicherheitsauswirkung). Der letztere Fall ist besonders gefährlich, da er kryptografische Operationen stillschweigend schwächt, während er normal zu funktionieren scheint.
Risiko
Unzureichende PRNG-Entropie erzeugt kritische Sicherheitslücken in mehreren Dimensionen. Wenn PRNGs während der Schlüsselgenerierung keine Entropie haben, können die resultierenden kryptografischen Schlüssel vorhergesagt oder per Brute-Force geknackt werden. Bitcoin-Wallets, die mit unzureichender Entropie generiert wurden, wurden ausgenutzt, um Kryptowährung zu stehlen. Session-Tokens und Authentifizierungs-Cookies werden vorhersagbar, was Session-Hijacking ermöglicht. SSL/TLS-Verbindungen, die schwache Entropie für den Schlüsselaustausch verwenden, können von Angreifern gebrochen werden, die die Session-Schlüssel rekonstruieren können. Das Risiko wird erhöht, weil diese Ausfälle oft still sind - Anwendungen produzieren weiterhin zufällig aussehende Ausgaben, die oberflächliche Inspektion bestehen, aber unter statistischer Analyse scheitern. Systeme, die Entropie erschöpfen und auf schwächere Quellen zurückgreifen (wie Math.random() anstelle eines CSPRNG), erzeugen besonders gefährliche Bedingungen, bei denen sich die Sicherheit stillschweigend verschlechtert.
Lösung
Verwenden Sie PRNGs, die für kryptografische Zwecke konzipiert und ordnungsgemäß mit hochwertiger Entropie gespeist werden. Wählen Sie FIPS 140-2-konforme Zufallszahlengeneratoren, wenn Compliance erforderlich ist. Implementieren Sie PRNGs, die automatisch aus Hardware-Entropiequellen neu seeden, wenn verfügbar. Verwenden Sie vom Betriebssystem bereitgestellte CSPRNGs wie /dev/urandom unter Unix oder CryptGenRandom unter Windows, die Entropiepools angemessen verwalten. Fallen Sie niemals auf schwache Zufallsquellen zurück, wenn die sichere Quelle ausfällt - stattdessen die Operation vollständig fehlschlagen lassen. Überwachen Sie Entropieniveaus in kritischen Systemen und alarmieren Sie, wenn die Entropie niedrig wird. Erwägen Sie Hardware-Zufallszahlengeneratoren (HRNGs) für Hochsicherheitsanwendungen. Entwerfen Sie Anwendungen so, dass sie Entropieerschöpfung elegant handhaben, ohne die Sicherheit zu beeinträchtigen.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Verfügbarkeit | Umfang: Verfügbarkeit Wenn ein PRNG seine Entropiequelle erschöpft und geschlossen ausfällt, können Anwendungen pausieren, blockieren oder abstürzen, während sie auf mehr Entropie warten, was Denial-of-Service verursacht. |
| Zugriffskontrolle | Umfang: Zugriffskontrolle Wenn der PRNG offen ausfällt, wenn die Entropie erschöpft ist, erzeugt er vorhersagbare Werte, die Authentifizierungs- und Autorisierungsmechanismen schwächen und unbefugten Zugriff ermöglichen. |
| Vertraulichkeit | Umfang: Vertraulichkeit Mit unzureichender PRNG-Entropie generierte Verschlüsselungsschlüssel können vorhergesagt oder per Brute-Force geknackt werden, was die Vertraulichkeit verschlüsselter Daten kompromittiert. |
Beispielcode
Anfälliger Code (Python/Java)
Die folgenden Beispiele demonstrieren unzureichende PRNG-Entropie:
# Anfällig: PRNG mit unzureichender Entropie-Behandlung
import random
import time
# Anfällig: Fallback auf schwachen Zufall bei Fehler
def anfällige_schlüssel_generierung():
try:
import secrets
return secrets.token_bytes(32)
except Exception:
# Anfällig: Fallback auf schwachen Zufall!
random.seed(time.time())
return bytes([random.randint(0, 255) for _ in range(32)])
# Anfällig: random-Modul für Krypto verwenden
def anfälliges_session_token():
# Anfällig: random-Modul ist nicht kryptografisch sicher
# Verwendet Mersenne Twister, der vorhersagbar ist
return format(random.getrandbits(128), '032x')
# Anfällig: Seeding mit unzureichender Entropie
def anfällige_prng_init():
# Anfällig: Nur 32 Bits Entropie aus Zeit
seed = int(time.time())
random.seed(seed)
return random
# Anfällig: PRNG ohne Reseeding
class AnfälligerStatischerPRNG:
def __init__(self):
# Anfällig: Einmal beim Start geseedet
self.rng = random.Random()
self.rng.seed(time.time())
def generieren(self, lange):
# Anfällig: Seedet nie neu, Entropie erschöpft sich über Zeit
return bytes([self.rng.randint(0, 255) for _ in range(lange)])
# Anfällig: Benutzerdefinierter PRNG mit schwacher Entropie
class AnfälligerLCG:
def __init__(self, seed=None):
# Anfällig: Linearer Kongruenzgenerator
# mit vorhersagbaren Parametern
self.state = seed or int(time.time())
def next(self):
# Anfällig: LCG ist vollständig vorhersagbar
self.state = (self.state * 1103515245 + 12345) & 0x7FFFFFFF
return self.state
// Anfällig: Unzureichende PRNG-Entropie in Java
import java.util.Random;
public class AnfälligerPRNG {
// Anfällig: java.util.Random für Sicherheit verwenden
private Random schwacherRandom = new Random();
// Anfällig: Fallback auf schwachen Zufall
public byte[] anfälligeSchlüsselGenerierung(int lange) {
byte[] key = new byte[lange];
try {
java.security.SecureRandom.getInstanceStrong().nextBytes(key);
} catch (Exception e) {
// Anfällig: Fallback auf schwachen Zufall!
schwacherRandom.nextBytes(key);
}
return key;
}
// Anfällig: Zeitbasiert geseedeter Random für Tokens
public String anfälligesToken() {
// Anfällig: java.util.Random hat nur 48 Bits Seed
Random r = new Random(System.currentTimeMillis());
return Long.toHexString(r.nextLong());
}
// Anfällig: Gemeinsame Random-Instanz
private static Random geteilterRandom = new Random();
public byte[] anfälligerGeteilterRandom(int lange) {
// Anfällig: Geteilte Instanz, vorhersagbar nach Beobachtung
byte[] result = new byte[lange];
geteilterRandom.nextBytes(result);
return result;
}
// Anfällig: Math.random() für Sicherheit
public String anfälligesMathRandom() {
// Anfällig: Math.random() ist nicht kryptografisch
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 32; i++) {
sb.append(String.format("%02x", (int)(Math.random() * 256)));
}
return sb.toString();
}
}
// Anfällig: Unzureichende PRNG-Entropie in JavaScript
// Dieses Muster wurde in echten Kryptowährungsbibliotheken gefunden
// Anfällig: Math.random() für Krypto verwenden
function anfälligeSchlüsselGenerierung(lange) {
const key = new Uint8Array(lange);
for (let i = 0; i < lange; i++) {
// Anfällig: Math.random() ist nicht kryptografisch sicher
key[i] = Math.floor(Math.random() * 256);
}
return key;
}
// Anfällig: Fallback auf Math.random()
function anfälligerSichererZufall(lange) {
try {
const crypto = require('crypto');
return crypto.randomBytes(lange);
} catch (e) {
// Anfällig: Stiller Fallback auf schwachen Zufall!
console.warn('Verwende Fallback-Zufall');
const result = Buffer.alloc(lange);
for (let i = 0; i < lange; i++) {
result[i] = Math.floor(Math.random() * 256);
}
return result;
}
}
// Anfällig: Geseedeter PRNG für Wallet-Generierung
function anfälligerWalletSeed() {
// Anfällig: Zeitstempel als einzige Entropiequelle verwenden
const seed = Date.now();
return customPRNG(seed);
}
// Anfällig: XorShift ohne ordnungsgemäßes Seeding
class AnfälligerXorShift {
constructor(seed) {
// Anfällig: Schwaches Seeding
this.state = seed || Date.now();
}
next() {
// Anfällig: Vorhersagbar bei bekanntem Zustand
let x = this.state;
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
this.state = x;
return x >>> 0;
}
}
Korrigierter Code (Python/Java)
# Korrigiert: Ordnungsgemäße PRNG-Entropie-Behandlung
import os
import secrets
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
# Korrigiert: Immer sicheren Zufall verwenden, bei Fehler abbrechen
def sichere_schlüssel_generierung(lange=32):
# Korrigiert: secrets-Modul verwendet System-CSPRNG
# Wirft Exception, wenn Entropie nicht verfügbar
return secrets.token_bytes(lange)
# Korrigiert: Ordnungsgemäße Session-Token-Generierung
def sicheres_session_token():
# Korrigiert: secrets.token_hex verwendet CSPRNG
return secrets.token_hex(32)
# Korrigiert: Kein Fallback auf schwachen Zufall
def sichere_zufalls_bytes(lange):
# Korrigiert: os.urandom ist der System-CSPRNG
# Wirft Exception anstatt Sicherheit zu degradieren
return os.urandom(lange)
# Korrigiert: PRNG mit ordnungsgemäßem Seeding und Reseeding
class SichererPRNG:
def __init__(self):
# Korrigiert: Aus System-Entropie initialisieren
self.reseed()
def reseed(self):
# Korrigiert: Frische Entropie vom System holen
self._state = os.urandom(32)
def generieren(self, lange):
# Korrigiert: AES-CTR für DRBG-Verhalten verwenden
nonce = os.urandom(16)
cipher = Cipher(algorithms.AES(self._state), modes.CTR(nonce))
encryptor = cipher.encryptor()
# Zufällige Bytes generieren
result = encryptor.update(bytes(lange))
# Korrigiert: Regelmäßig reseeden
self.reseed()
return result
# Korrigiert: Entropie-Überwachung
def sicher_mit_entropie_prüfung():
# Korrigiert: Unter Linux kann Entropiepool geprüft werden
try:
with open('/proc/sys/kernel/random/entropy_avail', 'r') as f:
entropie = int(f.read().strip())
if entropie < 256:
# Warten oder alarmieren, nicht mit schwacher Entropie fortfahren
raise RuntimeError(f"Unzureichende Entropie: {entropie}")
except FileNotFoundError:
pass # Nicht unter Linux, auf System verlassen
return os.urandom(32)
// Korrigiert: Ordnungsgemäße PRNG-Entropie-Behandlung in Java
import java.security.SecureRandom;
import java.security.NoSuchAlgorithmException;
public class SichererPRNG {
private SecureRandom secureRandom;
public SichererPRNG() throws NoSuchAlgorithmException {
// Korrigiert: Stärken SecureRandom verwenden
this.secureRandom = SecureRandom.getInstanceStrong();
}
// Korrigiert: Immer SecureRandom verwenden, bei Fehler abbrechen
public byte[] sichereSchlüsselGenerierung(int lange) throws NoSuchAlgorithmException {
byte[] key = new byte[lange];
// Korrigiert: Kein Fallback - fehlschlagen wenn Entropie nicht verfügbar
secureRandom.nextBytes(key);
return key;
}
// Korrigiert: Sichere Token-Generierung
public String sicheresToken() {
byte[] bytes = new byte[32];
secureRandom.nextBytes(bytes);
return bytesToHex(bytes);
}
// Korrigiert: Reseeding für langlebige Anwendungen erzwingen
public void reseed() {
// Korrigiert: generateSeed holt frische System-Entropie
byte[] seed = secureRandom.generateSeed(32);
secureRandom.setSeed(seed);
}
// Korrigiert: Thread-sicherer sicherer Zufall
public byte[] threadSichererZufall(int lange) {
// Korrigiert: ThreadLocalRandom für SecureRandom
byte[] result = new byte[lange];
SecureRandom.getInstanceStrong().nextBytes(result);
return result;
}
// Korrigiert: DRBG-basierter SecureRandom (Java 9+)
public SecureRandom getDRBGRandom() throws NoSuchAlgorithmException {
// Korrigiert: DRBG-Mechanismus mit Reseeding verwenden
return SecureRandom.getInstance("DRBG",
DrbgParameters.instantiation(256,
DrbgParameters.Capability.RESEED_ONLY,
null));
}
private String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
}
// Korrigiert: Ordnungsgemäße PRNG-Entropie in JavaScript
const crypto = require('crypto');
// Korrigiert: crypto.randomBytes verwenden, bei Nichtverfügbarkeit fehlschlagen
function sichereSchlüsselGenerierung(lange) {
// Korrigiert: crypto.randomBytes verwendet System-CSPRNG
// Wirft Fehler, wenn Entropie nicht verfügbar
return crypto.randomBytes(lange);
}
// Korrigiert: Kein Fallback auf Math.random()
function sichererZufall(lange) {
// Korrigiert: Vollständig fehlschlagen anstatt zu degradieren
if (!crypto || !crypto.randomBytes) {
throw new Error('Sicherer Zufall nicht verfügbar');
}
return crypto.randomBytes(lange);
}
// Korrigiert: Browser-kompatibler sicherer Zufall
function browserSichererZufall(lange) {
// Korrigiert: Web Crypto API verwenden
if (typeof window !== 'undefined' && window.crypto) {
const array = new Uint8Array(lange);
window.crypto.getRandomValues(array);
return array;
}
throw new Error('Sicherer Zufall in dieser Umgebung nicht verfügbar');
}
// Korrigiert: Sichere Wallet-Seed-Generierung
function sichererWalletSeed() {
// Korrigiert: Ordnungsgemäße Entropiequelle verwenden
const entropie = crypto.randomBytes(32);
// Zusätzliche Entropie vom System wenn verfügbar
const zeitstempel = Buffer.from(Date.now().toString());
const kombiniert = crypto.createHash('sha256')
.update(entropie)
.update(zeitstempel)
.digest();
return kombiniert;
}
// Korrigiert: Asynchroner sicherer Zufall mit expliziter Entropie-Prüfung
async function sichererZufallAsync(lange) {
return new Promise((resolve, reject) => {
crypto.randomBytes(lange, (err, buffer) => {
if (err) {
reject(new Error('Entropiegenerierung fehlgeschlagen: ' + err.message));
} else {
resolve(buffer);
}
});
});
}
Die Korrektur verwendet kryptografisch sichere PRNGs mit ordnungsgemäßen Entropiequellen und schlägt sicher fehl, wenn Entropie unzureichend ist.
Ausgenutzt in der Praxis
JavaScript-Kryptowährungsbibliothek (CVE-2019-1715)
Eine JavaScript-Kryptowährungsbibliothek fiel auf unsicheres Math.random() zurück, anstatt einen Fehler zu melden, wenn sicherer Zufall nicht verfügbar war, was die Generierung nicht-eindeutiger Bitcoin-Wallet-Schlüssel und den Diebstahl von Geldern ermöglichte.
Android SecureRandom-Schwachstelle (2013)
Androids Java SecureRandom-Implementierung hatte auf einigen Geräten unzureichende Entropie, was zu Bitcoin-Wallet-Kompromittierungen führte, bei denen dieselben Schlüssel auf verschiedenen Geräten generiert wurden.
Tools zum Testen/Ausnutzen
-
ent — Statistisches Entropie-Analyse-Tool.
-
NIST SP 800-90B Entropy Estimation — Offizielle NIST-Entropie-Bewertungstools.
-
rngtest — Zufallszahlengenerator-Test-Tool.
CVE-Beispiele
-
CVE-2019-1715 — Kryptowährungsbibliothek-Fallback auf Math.random().
-
CVE-2013-ANDROID — Android SecureRandom unzureichende Entropie.
Referenzen
-
MITRE Corporation. "CWE-332: Insufficient Entropy in PRNG." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/332.html
-
NIST. "Recommendation for Random Bit Generator (RBG) Constructions." SP 800-90C. https://csrc.nist.gov/publications/detail/sp/800-90c/draft
-
FIPS 140-2. "Security Requirements for Cryptographic Modules." https://csrc.nist.gov/publications/detail/fips/140/2/final