Numerischer Bereichsvergleich ohne Minimumprüfung

Beschreibung

Numerischer Bereichsvergleich ohne Minimumprüfung ist eine Eingabevalidierungs-Schwachstelle, bei der Software validiert, dass ein numerischer Wert einen Maximalgrenzwert nicht überschreitet, aber nicht verifiziert, dass er einen Minimalgrenzwert erfüllt, wie z.B. nicht-negativ zu sein. Diese Unterlassung ist besonders gefährlich, wenn vorzeichenbehaftete Integer oder Fließkommazahlen in Kontexten verwendet werden, die nur positive Werte erwarten. Negative Werte können unerwartetes Verhalten verursachen, einschließlich Zugriff auf Speicher vor Array-Grenzen, Allokation unerwarteter Speichermengen, Invertierung von Finanztransaktionslogik oder arithmetischem Überlauf bei Konvertierung zu vorzeichenlosen Typen.

Risiko

Diese Schwachstelle kann zu Buffer-Underflows, Speicherbeschädigung und Geschäftslogik-Umgehungen führen. Wenn ein negativer Array-Index verwendet wird, wird Speicher vor dem Array zugegriffen, was möglicherweise zu Informationsoffenlegung oder Codeausführung führt. Negative Werte, die an Speicherallokationsfunktionen übergeben werden, können zu großen Werten wrappen, was Allokationsfehler oder übermäßigen Speicherverbrauch verursacht. In Finanzanwendungen können negative Transaktionsbeträge die beabsichtigte Operation invertieren (Abhebung wird zur Einzahlung). Wenn vorzeichenbehaftete Werte zu vorzeichenlosen gecastet werden, werden negative Werte zu sehr großen positiven Werten, was zu massiven Pufferüberläufen oder Ressourcenerschöpfung führt.

Lösung

Validieren Sie immer sowohl minimale als auch maximale Grenzen für numerische Eingaben. Wenn nur positive Werte gültig sind, prüfen Sie explizit, dass Werte größer oder gleich Null sind. Erwägen Sie die Verwendung vorzeichenloser Integer-Typen (size_t, unsigned int), wenn negative Werte niemals gültig sind. Seien Sie sich impliziter Typkonvertierungen bewusst, die negative Werte maskieren können. Implementieren Sie Validierung an Vertrauensgrenzen, wo externe Daten ins System eintreten. Für Array-Indizes validieren Sie gegen sowohl 0 als auch die Array-Länge. Für Finanzwerte validieren Sie gegen sowohl minimale als auch maximale Transaktionslimits. Verwenden Sie Assertions in Debug-Builds, um Logikfehler zu erkennen.

Häufige Auswirkungen

AuswirkungDetails
IntegritätBereich: Integrität

Anwendungsdaten modifizieren - Negative Werte können unerwartete Datenmodifikationen oder Geschäftslogik-Inversionen verursachen.
VerfügbarkeitBereich: Verfügbarkeit

DoS: Ressourcenverbrauch - Negative Werte können übermäßigen Ressourcenverbrauch auslösen, wenn sie konvertiert oder in Berechnungen verwendet werden.
Vertraulichkeit, IntegritätBereich: Vertraulichkeit, Integrität

Speicher lesen/modifizieren - Negative Indizes oder Größen können auf Speicher außerhalb der beabsichtigten Grenzen zugreifen.

Beispielcode

Anfälliger Code

// Anfällig: Keine Minimumprüfung bei Paket-Header-Zähler
void vulnerable_parse_packet(packet_t *packet) {
    int numHeaders = packet->headers;

    // Prüft nur Maximum, nicht Minimum
    if (numHeaders > MAX_HEADERS) {
        error("Zu viele Headers");
        return;
    }

    // Anfällig: Negatives numHeaders verursacht Probleme
    // Wenn zu size_t für malloc gecastet, wird es zu riesiger positiver Zahl
    PacketHeader *headers = malloc(numHeaders * sizeof(PacketHeader));

    // Wenn Allokation mit beschädigter Größe "erfolgreich", tritt Überlauf auf
    for (int i = 0; i < numHeaders; i++) {
        parse_header(&headers[i], packet);
    }
}
// Anfällig: Array-Zugriff ohne Prüfung auf negativen Index
int vulnerable_get_element(int *array, int length, int index) {
    // Prüft nur obere Grenze
    if (index < length) {
        return array[index];  // Negativer Index liest vor dem Array!
    }
    return -1;
}

// Angriff: index = -5 liest Speicher 5 Positionen vor dem Array
// Anfällig: Finanzabhebung ohne Prüfung auf negative Werte
public class VulnerableBank {

    public boolean withdraw(Account account, double amount) {
        // Prüft nur maximales Abhebungslimit
        if (amount > MAXIMUM_WITHDRAWAL_LIMIT) {
            return false;
        }

        // Anfällig: Negativer Betrag verursacht Einzahlung stattdessen!
        account.setBalance(account.getBalance() - amount);
        // -(-100) = +100, also "Abheben" von -100 fügt 100 hinzu

        return true;
    }
}
# Anfällig: Substring ohne Prüfung auf negative Länge
def vulnerable_substring(string, start, length):
    # Prüft nur maximale Länge
    if length > MAX_SUBSTRING_LENGTH:
        length = MAX_SUBSTRING_LENGTH

    # Anfällig: Negative Länge oder Start verursacht Probleme
    return string[start:start + length]

# Python behandelt dies gnädig, aber in C:
# memcpy(dest, src + start, length) mit negativer Länge ist UB
// Anfällig: Schleifenzähler ohne Prüfung auf negative Werte
void vulnerable_copy_elements(int *dest, int *src, int count) {
    // Prüft nur Maximum
    if (count > MAX_ELEMENTS) {
        count = MAX_ELEMENTS;
    }

    // Anfällig: Negativer count
    // Im unsigned-Vergleich wird -1 zu riesiger positiver Zahl
    for (size_t i = 0; i < (size_t)count; i++) {
        dest[i] = src[i];  // Massiver Buffer Overflow!
    }
}
// Anfällig: Array-Slice ohne Validierung negativer Werte
function vulnerableSlice(array, start, count) {
    // Validiert count nur gegen Array-Länge
    if (count > array.length) {
        count = array.length;
    }

    // Anfällig: Negativer start oder count
    // JavaScript behandelt gnädig, aber Geschäftslogik möglicherweise nicht
    return array.slice(start, start + count);
}
// Anfällig: Größe verwendet in Speicherallokation
int vulnerable_allocate(int requested_size) {
    // Prüft nur Maximum
    if (requested_size > MAX_ALLOCATION_SIZE) {
        return -1;
    }

    // Anfällig: Negative Größe
    // malloc((size_t)-1) fordert ~4GB auf 32-Bit-Systemen an
    char *buffer = malloc(requested_size);
    if (!buffer) {
        return -1;
    }

    memset(buffer, 0, requested_size);  // Absturz oder Beschädigung
    return 0;
}

Korrigierter Code

// Korrigiert: Sowohl minimale als auch maximale Grenzen prüfen
void fixed_parse_packet(packet_t *packet) {
    int numHeaders = packet->headers;

    // Korrigiert: Beide Grenzen prüfen
    if (numHeaders < 0 || numHeaders > MAX_HEADERS) {
        error("Ungültige Header-Anzahl");
        return;
    }

    // Jetzt sicher zu allokieren
    PacketHeader *headers = malloc((size_t)numHeaders * sizeof(PacketHeader));
    if (!headers) {
        error("Allokation fehlgeschlagen");
        return;
    }

    for (int i = 0; i < numHeaders; i++) {
        parse_header(&headers[i], packet);
    }
}
// Korrigiert: Array-Index-Bereich validieren
int fixed_get_element(int *array, int length, int index) {
    // Korrigiert: Beide Grenzen prüfen
    if (index < 0 || index >= length) {
        return -1;  // Ungültiger Index
    }

    return array[index];
}

// Besser: Vorzeichenlose Typen verwenden wenn negative Werte ungültig sind
int better_get_element(int *array, size_t length, size_t index) {
    // size_t ist vorzeichenlos, also negative Werte unmöglich
    if (index >= length) {
        return -1;
    }
    return array[index];
}
// Korrigiert: Transaktionsbetragsgrenzen validieren
public class FixedBank {

    private static final double MINIMUM_TRANSACTION = 0.01;

    public boolean withdraw(Account account, double amount) {
        // Korrigiert: Sowohl Minimum als auch Maximum prüfen
        if (amount < MINIMUM_TRANSACTION) {
            return false;  // Lehnt negative und Null-Beträge ab
        }

        if (amount > MAXIMUM_WITHDRAWAL_LIMIT) {
            return false;
        }

        if (amount > account.getBalance()) {
            return false;  // Unzureichendes Guthaben
        }

        account.setBalance(account.getBalance() - amount);
        return true;
    }
}
# Korrigiert: Beide Grenzen für Substring validieren
def fixed_substring(string, start, length):
    # Korrigiert: Alle Parameter validieren
    if start < 0:
        start = 0

    if length < 0:
        return ""  # Oder ValueError auslösen

    if start > len(string):
        return ""

    # Am String-Ende begrenzen
    end = min(start + length, len(string))

    return string[start:end]

# Besser: Explizite Validierung mit Exceptions verwenden
def better_substring(string, start, length):
    if not isinstance(start, int) or not isinstance(length, int):
        raise TypeError("start und length müssen Integer sein")

    if start < 0 or length < 0:
        raise ValueError("start und length müssen nicht-negativ sein")

    if start > len(string):
        raise IndexError("start überschreitet String-Länge")

    return string[start:start + length]
// Korrigiert: Vorzeichenlosen Typ für count verwenden
void fixed_copy_elements(int *dest, int *src, size_t count) {
    // Korrigiert: Mit size_t sind negative Werte unmöglich
    // Aber trotzdem Maximum validieren
    if (count > MAX_ELEMENTS) {
        count = MAX_ELEMENTS;
    }

    // Sicher: count ist garantiert nicht-negativ
    for (size_t i = 0; i < count; i++) {
        dest[i] = src[i];
    }
}

// Wenn vorzeichenbehafteter Typ von API erforderlich:
void fixed_copy_signed(int *dest, int *src, int count) {
    // Korrigiert: Explizite Validierung
    if (count < 0) {
        return;  // Ungültiger count
    }

    if (count > MAX_ELEMENTS) {
        count = MAX_ELEMENTS;
    }

    for (int i = 0; i < count; i++) {
        dest[i] = src[i];
    }
}
// Korrigiert: Ordnungsgemäße Größenvalidierung für Allokation
int fixed_allocate(int requested_size) {
    // Korrigiert: Beide Grenzen prüfen
    if (requested_size < 0) {
        return -1;  // Ungültige Größe
    }

    if (requested_size > MAX_ALLOCATION_SIZE) {
        return -1;
    }

    // Sicherer Cast: wir haben nicht-negativ verifiziert
    char *buffer = malloc((size_t)requested_size);
    if (!buffer) {
        return -1;
    }

    memset(buffer, 0, (size_t)requested_size);
    return 0;
}

// Besser: size_t von Anfang an verwenden
int better_allocate(size_t requested_size) {
    // size_t ist vorzeichenlos, negative Werte unmöglich
    if (requested_size > MAX_ALLOCATION_SIZE) {
        return -1;
    }

    char *buffer = malloc(requested_size);
    if (!buffer) {
        return -1;
    }

    memset(buffer, 0, requested_size);
    return 0;
}
// Korrigiert: Umfassendes Validierungs-Utility
public class NumericValidator {

    public static void validateInRange(int value, int min, int max, String name) {
        if (value < min || value > max) {
            throw new IllegalArgumentException(
                String.format("%s muss zwischen %d und %d liegen, war %d",
                    name, min, max, value));
        }
    }

    public static void validateNonNegative(int value, String name) {
        if (value < 0) {
            throw new IllegalArgumentException(
                name + " muss nicht-negativ sein, war " + value);
        }
    }

    public static void validatePositive(int value, String name) {
        if (value <= 0) {
            throw new IllegalArgumentException(
                name + " muss positiv sein, war " + value);
        }
    }
}

// Verwendung:
public void processData(int count, int index) {
    NumericValidator.validateNonNegative(count, "count");
    NumericValidator.validateInRange(index, 0, count - 1, "index");
    // Jetzt sicher zu verwenden
}

Verwandte CWEs

  • CWE-1023: Unvollständiger Vergleich mit fehlenden Faktoren (Eltern)
  • CWE-119: Unzureichende Einschränkung von Operationen innerhalb der Grenzen eines Speicherpuffers (kann folgen)
  • CWE-124: Puffer-Unterschreitung ('Buffer Underflow') (kann folgen)
  • CWE-195: Signed-zu-Unsigned-Konvertierungsfehler (kann folgen)
  • CWE-129: Unzureichende Validierung von Array-Index (verwandt)

Referenzen

  1. MITRE Corporation. "CWE-839: Numeric Range Comparison Without Minimum Check." https://cwe.mitre.org/data/definitions/839.html
  2. CERT C Secure Coding Standard. "INT04-C. Enforce limits on integer values originating from tainted sources."
  3. CERT C Secure Coding Standard. "ARR30-C. Do not form or use out-of-bounds pointers or array subscripts."