Unsachgemäße Behandlung unzureichender Entropie in TRNG
Beschreibung
Unsachgemäße Behandlung unzureichender Entropie in TRNG ist eine Schwachstelle, die auftritt, wenn ein System, das einen True Random Number Generator (TRNG) verwendet, Situationen nicht ordnungsgemäß behandelt, in denen die Entropiequelle erschöpft oder nicht verfügbar ist. Im Gegensatz zu Pseudo-Zufallszahlengeneratoren (PRNGs), die unbegrenzte Ausgaben aus einem Seed erzeugen können, sind TRNGs auf physikalische Entropiequellen wie thermisches Rauschen, radioaktiven Zerfall oder Hardware-Timing-Variationen angewiesen, die Zufälligkeit mit begrenzter Rate generieren. Wenn Anwendungen mehr Zufallsdaten anfordern, als der TRNG liefern kann, kann unsachgemäße Behandlung dazu führen, dass die Anwendung unbegrenzt blockiert, abstürzt oder schlimmer noch, auf schwächere Zufallsquellen zurückfällt. Die Rate, mit der echte Zufallszahlen generiert werden können, ist inhärent begrenzt, was eine umsichtige Verwendung und elegante Behandlung von Erschöpfung kritisch macht.
Risiko
Unsachgemäße TRNG-Behandlung erzeugt sowohl Verfügbarkeits- als auch Sicherheitsrisiken. Wenn Anwendungen beim Warten auf TRNG-Entropie blockieren, können Angreifer absichtlich Entropiequellen erschöpfen, um Denial-of-Service zu verursachen - ein Server, der Session-IDs aus einem Hardware-TRNG generiert, könnte durch schnelles Öffnen vieler Verbindungen zum Stillstand gebracht werden. Wenn das System bei Entropieerschöpfung abstürzt, ermöglicht dies unkomplizierte DoS-Angriffe. Gefährlicher ist, wenn Systeme stillschweigend auf schwächere Zufallsquellen zurückfallen - die Sicherheit erscheint aufrechterhalten, während sie tatsächlich degradiert ist. Anwendungen, die den TRNG-Verbrauch nicht begrenzen, können andere Prozesse von Entropie aushungern. In eingebetteten Systemen mit begrenzten Entropiequellen kann unsachgemäße Behandlung ordnungsgemäße kryptografische Operationen vollständig verhindern. Die Schwachstelle ist besonders schwerwiegend in Szenarien, die kontinuierliche Zufallszahlengenerierung erfordern, wie TLS-Server oder hochvolumiges Session-Management.
Lösung
Entwerfen Sie Anwendungen so, dass sie TRNG-Entropieerschöpfung elegant behandeln, ohne die Sicherheit zu kompromittieren. Verwenden Sie nicht-blockierende Entropiequellen wie /dev/urandom anstelle von blockierendem /dev/random für die meisten kryptografischen Zwecke - moderne Systeme halten ausreichend Entropie in urandom aufrecht. Implementieren Sie Rate-Limiting für Operationen, die echte Zufallszahlen erfordern. Reihen Sie Zufallszahlenanforderungen in eine Warteschlange ein und verarbeiten Sie sie, wenn Entropie verfügbar wird, anstatt zu blockieren oder abzustürzen. Fallen Sie niemals stillschweigend auf schwächere Zufallsquellen zurück - warten Sie entweder auf ordnungsgemäße Entropie oder lassen Sie die Operation explizit fehlschlagen. Cachen Sie Zufallszahlen während Perioden hoher Entropieverfügbarkeit für die Verwendung während Erschöpfung. Erwägen Sie hybride Ansätze, bei denen TRNG-Ausgabe hochwertige PRNGs für Massen-Zufallszahlengenerierung speist. Überwachen Sie Entropiepool-Niveaus und alarmieren Sie, wenn sie unter sichere Schwellenwerte fallen.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Verfügbarkeit | Umfang: Verfügbarkeit Programme können abstürzen, blockieren oder hängen bleiben, wenn Zufallszahlenvorräte erschöpft sind, was Denial-of-Service verursacht. Angreifer können dies ausnutzen, indem sie absichtlich Entropiequellen erschöpfen. |
| Vertraulichkeit | Umfang: Vertraulichkeit Wenn Systeme auf schwächere Zufallsquellen zurückfallen, wenn TRNGs erschöpft sind, können kryptografische Schlüssel und andere sicherheitskritische Werte mit unzureichender Entropie generiert werden. |
| Zugriffskontrolle | Umfang: Zugriffskontrolle Während Entropieerschöpfung generierte Authentifizierungs-Tokens können vorhersagbar sein, wenn das System auf schwache Zufallsquellen degradiert, was Session-Hijacking oder Authentifizierungs-Bypass ermöglicht. |
Beispielcode
Anfälliger Code (C/Python)
Die folgenden Beispiele demonstrieren unsachgemäße TRNG-Behandlung:
// Anfällig: Unsachgemäße Behandlung von TRNG-Entropieerschöpfung
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
// Anfällig: Blockierendes Lesen von /dev/random
int anfällig_hole_zufall(unsigned char *buffer, size_t lange) {
// Anfällig: /dev/random blockiert, wenn Entropiepool leer ist
// Kann unter hoher Last zu unbestimmten Hängern führen
int fd = open("/dev/random", O_RDONLY);
if (fd < 0) return -1;
// Anfällig: Kein Timeout, kein Fallback, keine Fehlerbehandlung
ssize_t bytes = read(fd, buffer, lange);
close(fd);
return bytes == (ssize_t)lange ? 0 : -1;
}
// Anfällig: Session-ID-Generierung, die Entropie erschöpfen kann
void anfälliger_session_handler(int client_fd) {
unsigned char session_id[32];
// Anfällig: Jede Verbindung verbraucht 32 Bytes echten Zufall
// Angreifer kann Entropie mit vielen Verbindungen erschöpfen
if (anfällig_hole_zufall(session_id, sizeof(session_id)) != 0) {
// Anfällig: Absturz bei Entropieerschöpfung
fprintf(stderr, "Konnte keine Zufallsbytes holen, beende\n");
exit(1); // Anfällig: DoS durch Server-Absturz
}
// Session-ID an Client senden
write(client_fd, session_id, sizeof(session_id));
}
// Anfällig: Kein Rate-Limiting beim Entropieverbrauch
void anfällige_schlüsselgenerierungs_schleife() {
unsigned char key[32];
while (1) {
// Anfällig: Kontinuierlicher Entropieverbrauch
// Hungert andere Prozesse aus
anfällig_hole_zufall(key, sizeof(key));
// Schlüssel verarbeiten...
}
}
// Anfällig: Stiller Fallback auf schwachen Zufall
int anfälliger_fallback_zufall(unsigned char *buffer, size_t lange) {
int fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
if (fd < 0) {
// Anfällig: Fallback auf schwachen Zufall!
srand(time(NULL));
for (size_t i = 0; i < lange; i++) {
buffer[i] = rand() % 256;
}
return 0;
}
ssize_t bytes = read(fd, buffer, lange);
close(fd);
if (bytes < (ssize_t)lange) {
// Anfällig: Stiller Fallback für verbleibende Bytes
srand(time(NULL) ^ getpid());
for (size_t i = bytes; i < lange; i++) {
buffer[i] = rand() % 256;
}
}
return 0;
}
# Anfällig: Unsachgemäße TRNG-Behandlung in Python
import os
import random
import time
# Anfällig: Lesen von /dev/random (blockierend)
def anfälliger_echter_zufall(lange):
# Anfällig: Blockiert, wenn Entropie erschöpft
with open('/dev/random', 'rb') as f:
return f.read(lange)
# Anfällig: Server, der Entropie erschöpft
class AnfälligerServer:
def handle_connection(self, client):
# Anfällig: Jede Verbindung verwendet echten Zufall
# Kein Rate-Limiting beim Entropieverbrauch
session_id = anfälliger_echter_zufall(32)
client.send(session_id)
# Anfällig: Stiller Fallback
def anfällig_hole_zufall_mit_fallback(lange):
try:
# Versuche echten Zufall zu bekommen
with open('/dev/random', 'rb') as f:
# Nicht-blockierend setzen
import fcntl
flags = fcntl.fcntl(f.fileno(), fcntl.F_GETFL)
fcntl.fcntl(f.fileno(), fcntl.F_SETFL, flags | os.O_NONBLOCK)
data = f.read(lange)
if len(data) < lange:
# Anfällig: Rest mit schwachem Zufall füllen!
data += bytes([random.randint(0, 255)
for _ in range(lange - len(data))])
return data
except Exception:
# Anfällig: Vollständiger Fallback auf schwachen Zufall
random.seed(time.time())
return bytes([random.randint(0, 255) for _ in range(lange)])
# Anfällig: Absturz bei Entropieerschöpfung
def anfällige_kritische_schlüsselgen():
try:
with open('/dev/random', 'rb') as f:
return f.read(32)
except:
# Anfällig: Exception verursacht Prozessbeendigung
raise SystemExit("Kann keinen sicheren Schlüssel generieren - Entropie erschöpft")
// Anfällig: Unsachgemäße TRNG-Behandlung in Java
import java.io.*;
import java.security.*;
import java.util.*;
public class AnfälligerTRNG {
// Anfällig: Blockierendes Lesen von /dev/random
public byte[] anfälligHoleZufall(int lange) throws IOException {
// Anfällig: Blockiert unbegrenzt, wenn Entropie erschöpft
FileInputStream fis = new FileInputStream("/dev/random");
byte[] buffer = new byte[lange];
fis.read(buffer); // Anfällig: Kein Timeout
fis.close();
return buffer;
}
// Anfällig: Fallback auf schwachen Zufall
public byte[] anfälligMitFallback(int lange) {
byte[] buffer = new byte[lange];
try {
SecureRandom strong = SecureRandom.getInstanceStrong();
strong.nextBytes(buffer);
} catch (NoSuchAlgorithmException e) {
// Anfällig: Stiller Fallback!
new Random().nextBytes(buffer);
}
return buffer;
}
// Anfällig: Gesamte Entropie verbrauchen
public void anfälligerEntropieverschwender() throws Exception {
SecureRandom sr = SecureRandom.getInstanceStrong();
while (true) {
byte[] data = new byte[1024];
// Anfällig: Kontinuierlicher Verbrauch
sr.nextBytes(data);
// Hungert andere Prozesse aus
}
}
// Anfällig: Keine elegante Degradation
public byte[] anfälligAbsturzBeiFehler(int lange) {
try {
return SecureRandom.getInstanceStrong().generateSeed(lange);
} catch (Exception e) {
// Anfällig: Absturz statt eleganter Behandlung
throw new RuntimeException("Sicherheitsfehler - kann nicht fortfahren");
}
}
}
Korrigierter Code (C/Python)
// Korrigiert: Ordnungsgemäße TRNG-Behandlung
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
// Korrigiert: /dev/urandom für die meisten Zwecke verwenden
int sicher_hole_zufall(unsigned char *buffer, size_t lange) {
// Korrigiert: /dev/urandom blockiert nicht und ist für Krypto geeignet
int fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) return -1;
ssize_t total = 0;
while (total < (ssize_t)lange) {
ssize_t bytes = read(fd, buffer + total, lange - total);
if (bytes < 0) {
if (errno == EINTR) continue; // Bei Interrupt wiederholen
close(fd);
return -1;
}
total += bytes;
}
close(fd);
return 0;
}
// Korrigiert: Rate-limitierte TRNG-Nutzung
typedef struct {
unsigned char pool[256];
size_t pool_offset;
time_t letztes_auffüllen;
} EntropiePool;
static EntropiePool entropie_pool = {0};
int sicher_rate_limitierter_zufall(unsigned char *buffer, size_t lange) {
// Korrigiert: Pool höchstens einmal pro Sekunde auffüllen
time_t jetzt = time(NULL);
if (jetzt > entropie_pool.letztes_auffüllen) {
// Frische Entropie von urandom holen
if (sicher_hole_zufall(entropie_pool.pool, sizeof(entropie_pool.pool)) == 0) {
entropie_pool.pool_offset = 0;
entropie_pool.letztes_auffüllen = jetzt;
}
}
// Aus Pool bedienen
if (entropie_pool.pool_offset + lange <= sizeof(entropie_pool.pool)) {
memcpy(buffer, entropie_pool.pool + entropie_pool.pool_offset, lange);
entropie_pool.pool_offset += lange;
return 0;
}
// Pool erschöpft, direkt holen aber nicht blockieren
return sicher_hole_zufall(buffer, lange);
}
// Korrigiert: Elegante Behandlung von Entropieerschöpfung
int sichere_session_id(unsigned char *session_id, size_t lange) {
// Korrigiert: Zuerst urandom versuchen (blockiert nicht)
if (sicher_hole_zufall(session_id, lange) == 0) {
return 0;
}
// Korrigiert: Fehler zurückgeben statt Absturz oder Fallback
return -1;
}
// Korrigiert: Entropieverfügbarkeit überwachen
int prüfe_entropie_verfügbar(size_t benötigt) {
#ifdef __linux__
FILE *f = fopen("/proc/sys/kernel/random/entropy_avail", "r");
if (f) {
int verfügbar;
if (fscanf(f, "%d", &verfügbar) == 1) {
fclose(f);
return verfügbar >= (int)benötigt * 8; // Bytes in Bits konvertieren
}
fclose(f);
}
#endif
return 1; // Auf Nicht-Linux als verfügbar annehmen
}
# Korrigiert: Ordnungsgemäße TRNG-Behandlung in Python
import os
import secrets
import threading
import time
# Korrigiert: os.urandom verwenden, das Entropie ordnungsgemäß behandelt
def sichere_zufalls_bytes(lange):
# Korrigiert: os.urandom ist nicht-blockierend und kryptografisch sicher
return os.urandom(lange)
# Korrigiert: secrets-Modul verwenden
def sicheres_token():
# Korrigiert: secrets-Modul behandelt Entropie korrekt
return secrets.token_hex(32)
# Korrigiert: Rate-limitierter Entropiepool
class SichererEntropiePool:
def __init__(self, pool_größe=1024):
self._pool = bytearray(pool_größe)
self._offset = pool_größe # Leer starten
self._lock = threading.Lock()
self._auffüllen()
def _auffüllen(self):
# Korrigiert: Von urandom auffüllen
neue_entropie = os.urandom(len(self._pool))
with self._lock:
self._pool[:] = neue_entropie
self._offset = 0
def hole_bytes(self, lange):
with self._lock:
if self._offset + lange > len(self._pool):
self._auffüllen()
ergebnis = bytes(self._pool[self._offset:self._offset + lange])
self._offset += lange
return ergebnis
# Korrigiert: Server mit Rate-Limiting
class SichererServer:
def __init__(self):
self._entropie_pool = SichererEntropiePool()
self._rate_limiter = {}
def handle_connection(self, client_addr):
# Korrigiert: Rate-Limit pro Client
jetzt = time.time()
if client_addr in self._rate_limiter:
if jetzt - self._rate_limiter[client_addr] < 0.1: # 100ms Minimum
raise RateLimitError("Zu viele Anfragen")
self._rate_limiter[client_addr] = jetzt
# Korrigiert: Gepoolte Entropie verwenden
session_id = self._entropie_pool.hole_bytes(32)
return session_id
# Korrigiert: Explizites Fehlschlagen, keine stille Degradation
def sichere_schlüsselgenerierung():
try:
# Korrigiert: secrets verwenden, das nicht degradiert
return secrets.token_bytes(32)
except Exception as e:
# Korrigiert: Explizites Fehlschlagen mit Logging
import logging
logging.error(f"Schlüsselgenerierung fehlgeschlagen: {e}")
raise SecurityError("Kann keinen sicheren Schlüssel generieren") from e
# Korrigiert: Entropie-Gesundheit unter Linux prüfen
def prüfe_entropie_gesundheit():
try:
with open('/proc/sys/kernel/random/entropy_avail', 'r') as f:
verfügbar = int(f.read().strip())
return verfügbar >= 256 # Minimales sicheres Niveau
except:
return True # Nicht-Linux-Systeme behandeln anders
// Korrigiert: Ordnungsgemäße TRNG-Behandlung in Java
import java.security.*;
import java.util.concurrent.*;
public class SichererTRNG {
private final SecureRandom secureRandom;
private final byte[] entropiePool;
private int poolOffset;
private final Object poolLock = new Object();
public SichererTRNG() throws NoSuchAlgorithmException {
// Korrigiert: Standard SecureRandom verwenden, der Entropie behandelt
this.secureRandom = new SecureRandom();
this.entropiePool = new byte[1024];
this.poolOffset = entropiePool.length; // Leer starten
}
// Korrigiert: Nicht-blockierender Zufall mit ordnungsgemäßer Behandlung
public byte[] holeSicherenZufall(int lange) {
byte[] ergebnis = new byte[lange];
// Korrigiert: SecureRandom behandelt Entropie intern
secureRandom.nextBytes(ergebnis);
return ergebnis;
}
// Korrigiert: Rate-limitierter Pool für hochfrequente Anfragen
public byte[] holeGepooltenZufall(int lange) {
synchronized (poolLock) {
if (poolOffset + lange > entropiePool.length) {
// Pool auffüllen
secureRandom.nextBytes(entropiePool);
poolOffset = 0;
}
byte[] ergebnis = new byte[lange];
System.arraycopy(entropiePool, poolOffset, ergebnis, 0, lange);
poolOffset += lange;
return ergebnis;
}
}
// Korrigiert: Asynchrone Zufallsgenerierung mit Timeout
public byte[] holeZufallMitTimeout(int lange, long timeoutMs)
throws TimeoutException {
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<byte[]> future = executor.submit(() -> {
byte[] ergebnis = new byte[lange];
SecureRandom.getInstanceStrong().nextBytes(ergebnis);
return ergebnis;
});
try {
return future.get(timeoutMs, TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException e) {
// Korrigiert: Explizites Fehlschlagen
throw new SecurityException("Konnte keine Zufallsbytes generieren", e);
} finally {
executor.shutdownNow();
}
}
// Korrigiert: Elegante Degradation mit Benachrichtigung
public byte[] holeZufallElegant(int lange) {
try {
// Zuerst starken Zufall versuchen
return SecureRandom.getInstanceStrong().generateSeed(lange);
} catch (NoSuchAlgorithmException e) {
// Korrigiert: Standard SecureRandom verwenden, aber Fallback loggen
System.err.println("Warnung: Verwende Standard-SecureRandom");
byte[] ergebnis = new byte[lange];
secureRandom.nextBytes(ergebnis);
return ergebnis;
}
}
}
Die Korrektur verwendet nicht-blockierende Entropiequellen, implementiert Rate-Limiting und schlägt explizit fehl, anstatt stillschweigend zu degradieren.
Ausgenutzt in der Praxis
Eingebettete Geräte Entropieerschöpfung
Verschiedene eingebettete Geräte mit begrenzten Entropiequellen haben Abstürze oder Sicherheitsdegradation erfahren, wenn kryptografische Operationen verfügbare Entropie schneller erschöpften, als sie aufgefüllt werden könnte.
Server-DoS via Entropieerschöpfung
Webserver, die blockierende Lesevorgänge von /dev/random für Session-ID-Generierung verwenden, wurden Denial-of-Service-Angriffen ausgesetzt, indem schnell viele Verbindungen geöffnet wurden, um den Entropiepool zu erschöpfen.
Tools zum Testen/Ausnutzen
-
rngtest — Testet Zufallszahlengenerator-Qualität und Entropie.
-
haveged — Entropie-Harvesting-Daemon für Linux-Systeme.
-
Entropie-Überwachungsskripte — Skripte zur Überwachung von /proc/sys/kernel/random/entropy_avail.
CVE-Beispiele
Für dieses CWE sind keine spezifischen CVEs weitverbreitet dokumentiert, aber das Schwachstellenmuster erscheint in vielen eingebetteten Systemen und Serveranwendungen, die unsachgemäß auf blockierende TRNG-Quellen angewiesen sind.
Referenzen
-
MITRE Corporation. "CWE-333: Improper Handling of Insufficient Entropy in TRNG." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/333.html
-
NIST. "Recommendation for the Entropy Sources Used for Random Bit Generation." SP 800-90B. https://csrc.nist.gov/publications/detail/sp/800-90b/final
-
Linux Kernel Documentation. "Random Number Generation." https://www.kernel.org/doc/html/latest/admin-guide/hw-random.html