Unsachgemäße Validierung des angegebenen Eingabetyps

Beschreibung

Unsachgemäße Validierung des angegebenen Eingabetyps tritt auf, wenn ein Produkt eine Eingabe erwartet, die einem bestimmten Typ entsprechen soll, aber nicht validiert oder falsch validiert, ob die Eingabe tatsächlich diesem erwarteten Typ entspricht. Wenn Eingaben nicht dem erwarteten Typ entsprechen, können Angreifer unerwartete Fehler auslösen, falsche Aktionen verursachen oder latente Schwachstellen ausnutzen. Diese Schwachstelle tritt häufig in typunsicheren Sprachen oder solchen auf, die Typumwandlung/-konvertierung unterstützen.

Risiko

Unsachgemäße Typvalidierung hat schwerwiegende Sicherheitsauswirkungen. Type-Confusion-Angriffe sind möglich. Injection-Schwachstellen können auftreten. Integer-Overflows durch Typkonvertierung. Buffer Overflows durch Größenunterschiede. Umgehung der Geschäftslogik wird ermöglicht. Anwendungsabstürze können auftreten. Datenbeschädigung ist möglich. Sicherheitskontrollen können umgangen werden.

Lösung

Verwenden Sie eine Eingabevalidierungsstrategie nach dem Prinzip "bekannt Gutes akzeptieren" mit strikten Whitelists akzeptabler Eingaben. Validieren Sie alle relevanten Eigenschaften einschließlich Länge, Typ, Wertebereiche, Syntax und Geschäftsregelkonformität. Verwenden Sie stark typisierte Sprachen oder strikte Typprüfung. Validieren und konvertieren Sie Typen explizit vor der Verwendung. Denylists können zwar Angriffe erkennen, sind aber allein unzureichend.

Häufige Auswirkungen

AuswirkungDetails
SonstigesBereich: Sonstiges

Auswirkung variiert je nachdem, wie die Typvalidierungsfehler ausgenutzt werden.
IntegritätBereich: Integrität

Type Confusion kann zu Datenbeschädigung führen.
VertraulichkeitBereich: Vertraulichkeit

Falsche Typen können unbeabsichtigte Daten offenlegen.

Beispielcode und Lösung

Verwundbarer Code

# VERWUNDBAR: Keine Typvalidierung in Python

def vulnerable_process_age(age):
    # VERWUNDBAR: Keine Typprüfung
    # Könnte String, Liste, None usw. erhalten

    if age > 18:  # TypeError wenn age nicht vergleichbar ist
        return "Adult"
    return "Minor"

def vulnerable_calculate_discount(price, quantity):
    # VERWUNDBAR: Keine Typvalidierung
    # price könnte String "100; DROP TABLE users" sein
    # quantity könnte negativ oder nicht-numerisch sein

    total = price * quantity  # Unerwartetes Verhalten bei falschen Typen
    return total

def vulnerable_process_user_data(data):
    # VERWUNDBAR: Nimmt an, dass data ein Dict ist, ohne zu prüfen

    name = data['name']        # KeyError wenn fehlend, TypeError wenn kein Dict
    email = data['email']      # Gleiche Probleme
    age = data['age']          # Könnte String statt Int sein

    # Verarbeitung ohne Typvalidierung
    return f"{name} ({age}) - {email}"

# VERWUNDBAR: SQL-Query mit unvalidierter numerischer Eingabe
def vulnerable_get_user(user_id):
    # VERWUNDBAR: user_id könnte String mit Injection sein
    query = f"SELECT * FROM users WHERE id = {user_id}"
    return execute_query(query)

    # user_id = "1 OR 1=1" würde alle Benutzer offenlegen
// VERWUNDBAR: JavaScript Type-Coercion-Probleme

// VERWUNDBAR: Keine Typvalidierung
function vulnerableCalculateTotal(price, quantity) {
    // JavaScript konvertiert Typen, möglicherweise falsch
    return price * quantity;

    // "10" * "5" = 50 (String-zu-Number-Konvertierung)
    // "10" * "abc" = NaN
    // [1,2] * 3 = NaN
}

// VERWUNDBAR: Nimmt Array an ohne zu prüfen
function vulnerableProcessItems(items) {
    // VERWUNDBAR: Wenn items kein Array ist, wirft forEach Fehler
    items.forEach(item => {
        console.log(item.name);  // Nimmt auch an, dass item name-Eigenschaft hat
    });
}

// VERWUNDBAR: Objekteigenschaftszugriff ohne Typprüfung
function vulnerableGetUserAge(user) {
    // VERWUNDBAR: Keine Validierung des user-Objekts
    return user.profile.age;  // Wirft Fehler wenn user/profile null/undefined ist
}

// VERWUNDBAR: parseInt-Verhalten
function vulnerableParseUserId(idString) {
    // VERWUNDBAR: parseInt hat überraschendes Verhalten
    return parseInt(idString);

    // parseInt("42abc") = 42 (ignoriert nachfolgende Zeichen)
    // parseInt("0x10") = 16 (Hex-Parsing)
    // parseInt("08") könnte in älterem JS = 0 sein (Oktal)
}
// VERWUNDBAR: C Typbehandlungsprobleme

#include <stdio.h>
#include <stdlib.h>

// VERWUNDBAR: Keine Validierung des numerischen Strings
int vulnerable_parse_int(const char* str) {
    // VERWUNDBAR: atoi gibt 0 für ungültige Eingabe zurück
    // Kann "0" nicht von "ungültig" unterscheiden
    return atoi(str);
}

// VERWUNDBAR: Type Confusion mit void-Pointer
void vulnerable_process_data(void* data, int type) {
    // VERWUNDBAR: Vertraut dem Typ-Indikator des Aufrufers
    if (type == 1) {
        int* int_data = (int*)data;
        printf("Integer: %d\n", *int_data);
    } else if (type == 2) {
        char* str_data = (char*)data;
        printf("String: %s\n", str_data);
    }

    // Wenn Typ nicht mit tatsächlichen Daten übereinstimmt, undefiniertes Verhalten
    // Angreifer, der 'type' kontrolliert, kann Type Confusion verursachen
}

// VERWUNDBAR: Größentyp-Nichtübereinstimmung
void vulnerable_copy(char* dest, const char* src, int size) {
    // VERWUNDBAR: 'size' ist vorzeichenbehafteter int
    // Negativer size könnte Prüfungen umgehen oder große Kopie verursachen

    if (size < sizeof(dest)) {  // Vergleichsprobleme mit negativem Wert
        memcpy(dest, src, size);  // size wird zu size_t (unsigned) gecastet
    }
}

Sichere Lösung

# SICHER: Ordnungsgemäße Typvalidierung in Python

from typing import Dict, Any, Union
import numbers

def secure_process_age(age: Any) -> str:
    """Alter mit Typvalidierung verarbeiten."""
    # SICHER: Typ validieren
    if not isinstance(age, (int, float)):
        raise TypeError(f"Alter muss numerisch sein, erhalten {type(age).__name__}")

    # SICHER: Es ist eine vernünftige Zahl
    if not isinstance(age, numbers.Real):
        raise TypeError(f"Alter muss eine reelle Zahl sein")

    age_float = float(age)

    # SICHER: Bereich validieren
    if age_float < 0 or age_float > 150:
        raise ValueError(f"Alter muss zwischen 0 und 150 sein, erhalten {age}")

    if age_float >= 18:
        return "Adult"
    return "Minor"

def secure_calculate_discount(price: Any, quantity: Any) -> float:
    """Rabatt mit validierten Typen berechnen."""
    # SICHER: Preistyp validieren
    if not isinstance(price, (int, float)):
        raise TypeError(f"Preis muss numerisch sein, erhalten {type(price).__name__}")

    # SICHER: Mengentyp validieren
    if not isinstance(quantity, int):
        raise TypeError(f"Menge muss Integer sein, erhalten {type(quantity).__name__}")

    # SICHER: Bereiche validieren
    if price < 0:
        raise ValueError("Preis darf nicht negativ sein")

    if quantity < 0:
        raise ValueError("Menge darf nicht negativ sein")

    return float(price) * quantity

def secure_process_user_data(data: Any) -> str:
    """Benutzerdaten mit Typvalidierung verarbeiten."""
    # SICHER: data ist ein Dict
    if not isinstance(data, dict):
        raise TypeError(f"Daten müssen Dict sein, erhalten {type(data).__name__}")

    # SICHER: Erforderliche Schlüssel existieren
    required_keys = ['name', 'email', 'age']
    for key in required_keys:
        if key not in data:
            raise ValueError(f"Fehlender erforderlicher Schlüssel: {key}")

    # SICHER: Typen der Werte validieren
    if not isinstance(data['name'], str):
        raise TypeError("Name muss String sein")

    if not isinstance(data['email'], str):
        raise TypeError("E-Mail muss String sein")

    if not isinstance(data['age'], int):
        raise TypeError("Alter muss Integer sein")

    return f"{data['name']} ({data['age']}) - {data['email']}"

# SICHER: Parametrisierte Abfrage mit validierter Eingabe
def secure_get_user(user_id: Any):
    """Benutzer mit validierter ID abrufen."""
    # SICHER: Typ validieren
    if not isinstance(user_id, int):
        raise TypeError(f"Benutzer-ID muss Integer sein, erhalten {type(user_id).__name__}")

    # SICHER: Bereich validieren
    if user_id < 1:
        raise ValueError("Benutzer-ID muss positiv sein")

    # SICHER: Parametrisierte Abfrage verwenden
    query = "SELECT * FROM users WHERE id = %s"
    return execute_query(query, (user_id,))
// SICHER: JavaScript mit Typvalidierung

// SICHER: Typvalidierung mit expliziten Prüfungen
function secureCalculateTotal(price, quantity) {
    // SICHER: Preistyp validieren
    if (typeof price !== 'number' || Number.isNaN(price)) {
        throw new TypeError(`Preis muss Number sein, erhalten ${typeof price}`);
    }

    // SICHER: Mengentyp validieren
    if (!Number.isInteger(quantity)) {
        throw new TypeError(`Menge muss Integer sein, erhalten ${typeof quantity}`);
    }

    // SICHER: Bereiche validieren
    if (price < 0) {
        throw new RangeError('Preis darf nicht negativ sein');
    }

    if (quantity < 0) {
        throw new RangeError('Menge darf nicht negativ sein');
    }

    return price * quantity;
}

// SICHER: Array-Validierung vor Iteration
function secureProcessItems(items) {
    // SICHER: items ist Array
    if (!Array.isArray(items)) {
        throw new TypeError(`Items muss Array sein, erhalten ${typeof items}`);
    }

    items.forEach(item => {
        // SICHER: Jedes Item validieren
        if (typeof item !== 'object' || item === null) {
            throw new TypeError('Jedes Item muss ein Objekt sein');
        }

        if (typeof item.name !== 'string') {
            throw new TypeError('Item-Name muss String sein');
        }

        console.log(item.name);
    });
}

// SICHER: Sicherer Eigenschaftszugriff mit Validierung
function secureGetUserAge(user) {
    // SICHER: Benutzerobjekt validieren
    if (typeof user !== 'object' || user === null) {
        throw new TypeError('Benutzer muss ein Objekt sein');
    }

    if (typeof user.profile !== 'object' || user.profile === null) {
        throw new TypeError('Benutzerprofil muss ein Objekt sein');
    }

    if (typeof user.profile.age !== 'number') {
        throw new TypeError('Benutzeralter muss eine Zahl sein');
    }

    return user.profile.age;
}

// SICHER: Striktes Integer-Parsing
function secureParseUserId(idString) {
    // SICHER: Eingabetyp validieren
    if (typeof idString !== 'string') {
        throw new TypeError('ID muss String sein');
    }

    // SICHER: Format validieren (nur Ziffern)
    if (!/^\d+$/.test(idString)) {
        throw new Error('Ungültiges Benutzer-ID-Format');
    }

    const id = parseInt(idString, 10);  // SICHER: Expliziter Radix

    // SICHER: Ergebnis validieren
    if (Number.isNaN(id) || id < 1) {
        throw new Error('Ungültige Benutzer-ID');
    }

    return id;
}
// SICHER: C mit ordnungsgemäßer Typvalidierung

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <stdbool.h>

// SICHER: Validiertes Integer-Parsing
bool secure_parse_int(const char* str, int* result) {
    if (str == NULL || result == NULL) {
        return false;
    }

    // SICHER: Auf leeren String prüfen
    if (*str == '\0') {
        return false;
    }

    char* endptr;
    errno = 0;

    // SICHER: strtol für ordnungsgemäße Fehlererkennung verwenden
    long val = strtol(str, &endptr, 10);

    // SICHER: Auf Konvertierungsfehler prüfen
    if (errno == ERANGE) {
        return false;  // Überlauf/Unterlauf
    }

    if (endptr == str) {
        return false;  // Keine Ziffern gefunden
    }

    if (*endptr != '\0') {
        return false;  // Nachfolgende Zeichen
    }

    // SICHER: Int-Bereich prüfen
    if (val < INT_MIN || val > INT_MAX) {
        return false;
    }

    *result = (int)val;
    return true;
}

// SICHER: Typsichere Datenverarbeitung
typedef enum {
    DATA_TYPE_INT = 1,
    DATA_TYPE_STRING = 2
} data_type_t;

typedef struct {
    data_type_t type;
    union {
        int int_value;
        char str_value[256];
    } data;
} typed_data_t;

void secure_process_data(const typed_data_t* data) {
    if (data == NULL) {
        return;
    }

    // SICHER: Typ ist Teil der Datenstruktur, kein separater Parameter
    switch (data->type) {
        case DATA_TYPE_INT:
            printf("Integer: %d\n", data->data.int_value);
            break;

        case DATA_TYPE_STRING:
            printf("String: %s\n", data->data.str_value);
            break;

        default:
            fprintf(stderr, "Unbekannter Datentyp: %d\n", data->type);
            break;
    }
}

// SICHER: Ordnungsgemäße Größentyp-Behandlung
bool secure_copy(char* dest, size_t dest_size, const char* src, size_t copy_size) {
    // SICHER: Alle Parameter werden validiert
    if (dest == NULL || src == NULL) {
        return false;
    }

    // SICHER: size_t (unsigned) für Größen verwenden
    if (copy_size == 0) {
        return true;  // Nichts zu kopieren
    }

    // SICHER: Grenzprüfung
    if (copy_size > dest_size) {
        return false;  // Würde Ziel überlaufen
    }

    memcpy(dest, src, copy_size);
    return true;
}

CVE-Beispiele

  • CVE-2024-37032: LLM-Tool hat Digest-Format nicht validiert, was Path Traversal durch Type Confusion ermöglichte.
  • CVE-2008-2223: SQL Injection durch unsachgemäß validierte numerische ID - String-Eingabe wurde verwendet, wo Integer erwartet wurde.

Verwandte CWEs

  • CWE-20: Unsachgemäße Eingabevalidierung (übergeordnet)
  • CWE-843: Zugriff auf Ressource mit inkompatiblem Typ (Type Confusion) (verwandt)
  • CWE-136: Typfehler (Kategorie)
  • CWE-1215: Datenvalidierungsprobleme (Kategorie)

Referenzen

  1. MITRE Corporation. "CWE-1287: Improper Validation of Specified Type of Input." https://cwe.mitre.org/data/definitions/1287.html
  2. OWASP. "Input Validation Cheat Sheet"
  3. CERT. "Type Safety Guidelines"