Übermäßige Iteration

Beschreibung

Übermäßige Iteration ist eine Ressourcenverbrauchs-Schwachstelle, bei der Software eine Iteration oder Schleife durchführt, ohne die Anzahl der Schleifendurchläufe ausreichend zu begrenzen. Wenn Schleifengrenzen durch externe Eingaben beeinflusst werden oder von ungeprüften Werten abgeleitet sind, können Angreifer die Anwendung dazu bringen, übermäßig CPU-Zyklen, Speicher oder andere Ressourcen zu verbrauchen. Die Schleife muss nicht unendlich sein, um Schäden anzurichten - die Auswirkung hängt von den pro Iteration verbrauchten Ressourcen und der Gesamtzahl der Iterationen ab. Selbst endliche Schleifen können Denial of Service verursachen, wenn sie Millionen von Iterationen laufen oder teure Operationen in jedem Zyklus durchführen.

Risiko

Diese Schwachstelle ermöglicht Denial-of-Service-Angriffe durch Erschöpfung von Systemressourcen. Angreifer, die Schleifengrenzen durch Eingabeparameter, Dateiinhalte, Netzwerkdaten oder andere Kanäle beeinflussen können, können Anwendungen nicht mehr reaktionsfähig machen. CPU-gebundene Schleifen können andere Prozesse und Threads aushungern. Speicherverbrauchende Schleifen können Out-of-Memory-Bedingungen und Abstürze auslösen. In Servern kann übermäßige Iteration die Antwortzeiten für alle Benutzer verlangsamen oder Thread-Pools erschöpfen. Die Schwachstelle ist besonders gefährlich bei der Verarbeitung nicht vertrauenswürdiger Daten wie Netzwerkpaketen, Dateiformaten oder Benutzereingaben, bei denen Längen- oder Zählerfelder die Iteration steuern.

Lösung

Validieren und begrenzen Sie immer Schleifengrenzen, besonders wenn sie von externen Eingaben abgeleitet sind. Implementieren Sie maximale Iterationslimits, die für den Anwendungskontext geeignet sind. Verwenden Sie Timeout-Mechanismen, um lang laufende Schleifen abzubrechen. Validieren Sie Eingabeparameter, bevor Sie sie in Schleifenbedingungen verwenden - lehnen Sie Null- oder negative Werte ab, die unendliche Schleifen verursachen könnten. Überwachen Sie den Ressourcenverbrauch und implementieren Sie Circuit-Breaker für teure Operationen. Erwägen Sie asynchrone Verarbeitung mit Abbruchunterstützung für lang laufende Iterationen. Stellen Sie sicher, dass Schleifenvariablen ordnungsgemäß aktualisiert werden, um Fortschritt zur Beendigung zu machen.

Häufige Auswirkungen

AuswirkungDetails
VerfügbarkeitBereich: Verfügbarkeit

Ressourcenverbrauch - Übermäßige Schleifen verbrauchen unerwartete Mengen an CPU-Zyklen und Speicher, was die Systemleistung verschlechtert.
VerfügbarkeitBereich: Verfügbarkeit

DoS: Langsame Antwort - Software-Betrieb verlangsamt sich, was verlängerte Antwortzeiten für Benutzer verursacht.
VerfügbarkeitBereich: Verfügbarkeit

DoS: Absturz, Beendigung oder Neustart - Ressourcenerschöpfung (z.B. Speicherüberlauf) kann das Programm zum Absturz bringen.

Beispielcode

Anfälliger Code

// Anfällig: Rekursive Funktion mit unbegrenzter Tiefe
void vulnerable_recursive(int flag) {
    if (flag == 1) {
        // ... etwas tun ...
    }

    // Anfällig: flag wird nie geändert, verursacht unendliche Rekursion
    vulnerable_recursive(flag);

    // Stack wird überlaufen
}
// Anfällig: Schleifengrenze hängt von unvalidiertem Parameter ab
public class VulnerableInventory {

    public boolean isReorder(int currentCount, int rateSold) {
        boolean isReorder = false;

        // Anfällig: Wenn rateSold 0 ist, läuft dies ewig!
        while (currentCount > 0) {
            if (currentCount < 10) {
                isReorder = true;
            }
            currentCount = currentCount - rateSold;
        }

        return isReorder;
    }
}
# Anfällig: Benutzer-kontrollierte Iterationszahl
def vulnerable_process_items(count):
    # Anfällig: Keine Begrenzung von count
    # Angreifer kann count = 1000000000 setzen
    for i in range(count):
        expensive_operation()  # DoS via CPU-Erschöpfung
// Anfällig: Schleife verarbeitet Netzwerkdaten ohne Größenlimits
void vulnerable_parse_records(char *data, int num_records) {
    // Anfällig: num_records kommt aus nicht vertrauenswürdigen Daten
    // Angreifer sendet num_records = MAX_INT
    for (int i = 0; i < num_records; i++) {
        process_record(data + (i * RECORD_SIZE));
    }
}
// Anfällig: Keine Begrenzung der Rekursionstiefe
function vulnerableFlatten(arr) {
    let result = [];
    for (let item of arr) {
        if (Array.isArray(item)) {
            // Anfällig: Tief verschachtelte Arrays verursachen Stack-Überlauf
            result = result.concat(vulnerableFlatten(item));
        } else {
            result.push(item);
        }
    }
    return result;
}
// Angriff: Tief verschachteltes Array erstellen [[[[...]]]]
// Anfällig: Verarbeitung von Zip-Datei mit übermäßig vielen Einträgen
<?php
function vulnerable_extract($zipFile) {
    $zip = new ZipArchive;
    $zip->open($zipFile);

    // Anfällig: Keine Begrenzung der Anzahl der Einträge
    for ($i = 0; $i < $zip->numFiles; $i++) {
        $filename = $zip->getNameIndex($i);
        extract_file($zip, $filename);
    }
    $zip->close();
}

// Angriff: Zip-Bombe mit Millionen kleiner Dateien
?>
// Anfällig: While-Schleife mit Fließkomma-Vergleich
void vulnerable_float_loop(double start, double end, double step) {
    double value = start;

    // Anfällig: Fließkomma-Fehler können verhindern, dass 'end' erreicht wird
    while (value != end) {
        process(value);
        value += step;  // Akkumulierter Fehler bedeutet, dass value niemals genau end entspricht
    }
}

Korrigierter Code

// Korrigiert: Rekursionstiefe begrenzen
#define MAX_RECURSION_DEPTH 100

void fixed_recursive(int flag, int depth) {
    // Korrigiert: Rekursionstiefe prüfen
    if (depth > MAX_RECURSION_DEPTH) {
        return;  // Stack-Überlauf verhindern
    }

    if (flag == 1) {
        // ... etwas tun ...
        return;  // Korrigiert: Tatsächliche Abbruchbedingung
    }

    fixed_recursive(flag - 1, depth + 1);  // Korrigiert: Fortschritt zur Beendigung
}
// Korrigiert: Schleifenparameter validieren
public class FixedInventory {

    private static final int MAX_ITERATIONS = 10000;

    public boolean isReorder(int currentCount, int rateSold) {
        // Korrigiert: rateSold validieren um unendliche Schleife zu verhindern
        if (rateSold < 1) {
            throw new IllegalArgumentException("rateSold muss positiv sein");
        }

        // Korrigiert: Auch maximale Iterationsprüfung hinzufügen
        int iterations = 0;
        boolean isReorder = false;

        while (currentCount > 0 && iterations < MAX_ITERATIONS) {
            if (currentCount < 10) {
                isReorder = true;
            }
            currentCount = currentCount - rateSold;
            iterations++;
        }

        return isReorder;
    }
}
# Korrigiert: Iterationszahl begrenzen
MAX_ITEMS = 10000

def fixed_process_items(count):
    # Korrigiert: Maximales Limit erzwingen
    if count < 0:
        raise ValueError("count muss nicht-negativ sein")

    actual_count = min(count, MAX_ITEMS)

    for i in range(actual_count):
        expensive_operation()

    if count > MAX_ITEMS:
        logging.warning(f"Iteration von {count} auf {MAX_ITEMS} gekürzt")
// Korrigiert: Datensatzzahl gegen Datengröße validieren
#define MAX_RECORDS 10000

int fixed_parse_records(char *data, size_t data_size, int num_records) {
    // Korrigiert: num_records validieren
    if (num_records < 0 || num_records > MAX_RECORDS) {
        return -1;  // Ungültige Anzahl
    }

    // Korrigiert: Prüfen, dass data_size num_records aufnehmen kann
    size_t required_size = (size_t)num_records * RECORD_SIZE;
    if (required_size > data_size) {
        return -1;  // Daten zu klein
    }

    for (int i = 0; i < num_records; i++) {
        process_record(data + (i * RECORD_SIZE));
    }

    return 0;
}
// Korrigiert: Rekursionstiefe für flatten begrenzen
function fixedFlatten(arr, maxDepth = 10) {
    if (maxDepth < 0) {
        throw new Error('Maximale Verschachtelungstiefe überschritten');
    }

    let result = [];
    for (let item of arr) {
        if (Array.isArray(item)) {
            // Korrigiert: Tiefenlimit dekrementieren
            result = result.concat(fixedFlatten(item, maxDepth - 1));
        } else {
            result.push(item);
        }
    }
    return result;
}

// Alternative: Iterativer Ansatz mit explizitem Stack
function fixedFlattenIterative(arr, maxIterations = 100000) {
    const result = [];
    const stack = [...arr];
    let iterations = 0;

    while (stack.length > 0) {
        if (++iterations > maxIterations) {
            throw new Error('Maximale Iterationen überschritten');
        }

        const item = stack.pop();
        if (Array.isArray(item)) {
            stack.push(...item);
        } else {
            result.push(item);
        }
    }

    return result.reverse();
}
// Korrigiert: Anzahl der verarbeiteten Dateien begrenzen
<?php
define('MAX_ZIP_FILES', 1000);

function fixed_extract($zipFile) {
    $zip = new ZipArchive;
    $zip->open($zipFile);

    // Korrigiert: Anzahl der Dateien begrenzen
    $numFiles = min($zip->numFiles, MAX_ZIP_FILES);

    for ($i = 0; $i < $numFiles; $i++) {
        $filename = $zip->getNameIndex($i);
        extract_file($zip, $filename);
    }

    if ($zip->numFiles > MAX_ZIP_FILES) {
        error_log("Warnung: Zip-Datei gekürzt, hatte " . $zip->numFiles . " Dateien");
    }

    $zip->close();
}
?>
// Korrigiert: Epsilon-Vergleich für Fließkomma-Schleifen verwenden
#include <math.h>

#define MAX_ITERATIONS 1000000

void fixed_float_loop(double start, double end, double step) {
    double value = start;
    int iterations = 0;

    // Korrigiert: Epsilon-Vergleich und Iterationslimit verwenden
    double epsilon = fabs(step) * 0.001;

    while (fabs(value - end) > epsilon && iterations < MAX_ITERATIONS) {
        process(value);
        value += step;
        iterations++;
    }

    if (iterations >= MAX_ITERATIONS) {
        log_warning("Fließkomma-Schleife erreichte maximale Iterationen");
    }
}

Verwandte CWEs

  • CWE-691: Unzureichendes Kontrollfluss-Management (Eltern)
  • CWE-835: Schleife mit unerreichbarer Abbruchbedingung (Kind - Endlosschleife)
  • CWE-674: Unkontrollierte Rekursion (Kind)
  • CWE-606: Ungeprüfter Input für Schleifenbedingung (verwandt)
  • CWE-400: Unkontrollierter Ressourcenverbrauch (verwandt)

Referenzen

  1. MITRE Corporation. "CWE-834: Excessive Iteration." https://cwe.mitre.org/data/definitions/834.html
  2. OWASP. "Denial of Service." https://owasp.org/www-community/attacks/Denial_of_Service
  3. CERT C Secure Coding Standard. "MSC17-C. Finish every set of statements associated with a case label with a break statement."