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

AuswirkungDetails
VerfügbarkeitUmfang: 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.
VertraulichkeitUmfang: 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.
ZugriffskontrolleUmfang: 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

  1. MITRE Corporation. "CWE-333: Improper Handling of Insufficient Entropy in TRNG." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/333.html

  2. NIST. "Recommendation for the Entropy Sources Used for Random Bit Generation." SP 800-90B. https://csrc.nist.gov/publications/detail/sp/800-90b/final

  3. Linux Kernel Documentation. "Random Number Generation." https://www.kernel.org/doc/html/latest/admin-guide/hw-random.html