Unsachgemäße Validierung der syntaktischen Korrektheit von Eingaben

Beschreibung

Unsachgemäße Validierung der syntaktischen Korrektheit von Eingaben tritt auf, wenn ein Produkt eine Eingabe erhält, die einer bestimmten Syntax folgen soll, aber die Konformität nicht validiert oder falsch validiert. Komplexe Eingaben müssen oft bestimmte Syntaxanforderungen erfüllen -- sei es für Datenformate, Auszeichnungssprachen oder Programmiersprachen. Ohne ordnungsgemäße Validierung nicht vertrauenswürdiger Eingaben können Angreifer Parsing-Fehler, unerwartete Fehler oder latente Schwachstellen ausnutzen, die mit korrekt formatierten Daten nicht zugänglich wären.

Risiko

Unsachgemäße syntaktische Validierung hat schwerwiegende Sicherheitsauswirkungen. Parsing-Fehler können auftreten. Injection-Angriffe werden möglich. XML/JSON-Parsing-Exploits werden ermöglicht. Konfigurationsdatei-Manipulation. Protokollebene-Angriffe sind möglich. Anwendungsabstürze können auftreten. Sicherheitskontrollen können umgangen werden. Datenbeschädigung kann resultieren.

Lösung

Implementieren Sie eine Validierung nach dem Prinzip "bekannt Gutes akzeptieren" -- pflegen Sie strikte Listen akzeptabler Eingaben, die den Spezifikationen entsprechen. Lehnen Sie nicht-konforme Daten ab oder transformieren Sie sie entsprechend. Überprüfen Sie Länge, Typ, zulässige Bereiche, fehlende/zusätzliche Eingaben, Syntax und Geschäftsregeln. Verwenden Sie Schema-Validierung für strukturierte Formate. Setzen Sie Parser-Generatoren für komplexe Syntax ein.

Häufige Auswirkungen

AuswirkungDetails
SonstigesBereich: Sonstiges

Auswirkung variiert - von Abstürzen bis zu Sicherheitsumgehungen.
IntegritätBereich: Integrität

Injection-Angriffe können Daten oder Verhalten modifizieren.
VerfügbarkeitBereich: Verfügbarkeit

Fehlerhafte Eingaben können Parser zum Absturz bringen.

Beispielcode und Lösung

Verwundbarer Code

// VERWUNDBAR: XML-Parsing ohne Validierung

import javax.xml.parsers.*;
import org.w3c.dom.*;
import java.io.*;

public class VulnerableXmlParser {

    // VERWUNDBAR: Keine XML-Schema-Validierung
    public Document parseXml(File xmlFile) throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

        // VERWUNDBAR: Validierung deaktiviert
        factory.setValidating(false);

        // VERWUNDBAR: Kein Schema gesetzt
        // factory.setSchema(...);

        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.parse(xmlFile);

        // Parst jedes wohlgeformte XML, auch wenn es nicht der erwarteten Struktur entspricht
        // Angreifer kann unerwartete Elemente oder Attribute einschleusen
        return doc;
    }

    // VERWUNDBAR: JSON-Parsing ohne Schema-Validierung
    public void processJsonConfig(String jsonInput) {
        // VERWUNDBAR: Keine Validierung gegen erwartetes Schema

        JSONObject config = new JSONObject(jsonInput);

        // Nimmt spezifische Struktur ohne Verifizierung an
        String serverUrl = config.getString("serverUrl");  // Existiert möglicherweise nicht
        int port = config.getInt("port");                  // Könnte String sein

        // Angreifer kann fehlerhaftes JSON oder unerwartete Typen liefern
    }
}
// VERWUNDBAR: Protokoll-Parsing ohne Syntaxvalidierung

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

// VERWUNDBAR: HTTP-Request-Parsing ohne ordnungsgemäße Validierung
void vulnerable_parse_http_request(const char* request) {
    char method[16];
    char path[256];
    char version[16];

    // VERWUNDBAR: Einfaches sscanf ohne Validierung
    int parsed = sscanf(request, "%s %s %s", method, path, version);

    // VERWUNDBAR: Keine Validierung des HTTP-Versionsformats
    // version könnte alles sein, nicht nur "HTTP/1.0" oder "HTTP/1.1"

    // VERWUNDBAR: Keine Validierung der Methode
    // method könnte ungültige Zeichen enthalten

    // VERWUNDBAR: Keine Validierung des Pfads
    // path könnte bösartige Sequenzen enthalten

    if (parsed == 3) {
        process_request(method, path, version);
    }
    // Fehlende ordnungsgemäße Fehlerbehandlung
}

// VERWUNDBAR: IP-Adress-Parsing ohne ordnungsgemäße Formatvalidierung
int vulnerable_parse_ip(const char* ip_string, uint32_t* ip_out) {
    // VERWUNDBAR: inet_addr akzeptiert Oktal-/Hex-Notation
    // "0x7f.0.0.1" oder "0177.0.0.1" könnten Filter umgehen

    *ip_out = inet_addr(ip_string);
    return (*ip_out != INADDR_NONE);

    // Filter, der auf "127.0.0.1" prüft, würde "0x7f.0.0.1" übersehen
}

// VERWUNDBAR: E-Mail-Validierung mit unzureichender Syntaxprüfung
int vulnerable_validate_email(const char* email) {
    // VERWUNDBAR: Prüft nur auf @-Symbol
    if (strchr(email, '@') != NULL) {
        return 1;  // "Gültig"
    }
    return 0;

    // Akzeptiert: "not valid@", "@invalid", "a@b", usw.
    // Viele syntaktisch ungültige E-Mails bestehen diese Prüfung
}
# VERWUNDBAR: Eingabe-Parsing ohne Syntaxvalidierung

import re
import json

# VERWUNDBAR: URL-Parsing ohne ordnungsgemäße Validierung
def vulnerable_parse_url(url):
    # VERWUNDBAR: Einfacher Regex validiert nicht die vollständige URL-Syntax
    match = re.match(r'https?://(.+)', url)

    if match:
        return match.group(1)  # Gibt alles nach dem Protokoll zurück
    return None

    # Akzeptiert fehlerhafte URLs wie "http://ungültige url mit leerzeichen"
    # Validiert keine Hostname-Syntax, Port-Nummern usw.

# VERWUNDBAR: Datum-Parsing ohne Formatvalidierung
def vulnerable_parse_date(date_str):
    # VERWUNDBAR: Keine Formatvalidierung
    parts = date_str.split('-')

    if len(parts) == 3:
        year, month, day = parts
        return {
            'year': int(year),   # Könnte bei nicht-numerischen Werten fehlschlagen
            'month': int(month),  # Könnte 13, -1, usw. sein
            'day': int(day)       # Könnte 32, 0, usw. sein
        }
    return None

    # Akzeptiert: "2024-13-45", "0-0-0", "-1--2--3"

# VERWUNDBAR: SQL-ähnliches Query-Parsing
def vulnerable_parse_query(query):
    # VERWUNDBAR: Keine Syntaxvalidierung
    # Akzeptiert jede Zeichenkette als "Query"

    if 'SELECT' in query.upper():
        # Als Select-Query verarbeiten
        pass
    elif 'INSERT' in query.upper():
        # Als Insert-Query verarbeiten
        pass

    # Keine echte SQL-Syntaxvalidierung
    # Angreifer kann fehlerhafte Queries einschleusen

Sichere Lösung

// SICHER: XML-Parsing mit ordnungsgemäßer Validierung

import javax.xml.parsers.*;
import javax.xml.validation.*;
import org.w3c.dom.*;
import org.xml.sax.*;
import java.io.*;

public class SecureXmlParser {

    // SICHER: XML-Parsing mit Schema-Validierung
    public Document parseXml(File xmlFile, File schemaFile) throws Exception {
        // Schema laden
        SchemaFactory schemaFactory = SchemaFactory.newInstance(
            XMLConstants.W3C_XML_SCHEMA_NS_URI
        );
        Schema schema = schemaFactory.newSchema(schemaFile);

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

        // SICHER: Validierung aktivieren
        factory.setValidating(true);
        factory.setNamespaceAware(true);
        factory.setSchema(schema);

        // SICHER: Sicherheitshärtung
        factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
        factory.setFeature(
            "http://apache.org/xml/features/disallow-doctype-decl", true);

        DocumentBuilder builder = factory.newDocumentBuilder();

        // SICHER: Fehlerbehandlung für Validierungsfehler
        builder.setErrorHandler(new ErrorHandler() {
            @Override
            public void error(SAXParseException e) throws SAXException {
                throw e;  // Bei Validierungsfehler abbrechen
            }

            @Override
            public void fatalError(SAXParseException e) throws SAXException {
                throw e;
            }

            @Override
            public void warning(SAXParseException e) {
                System.err.println("Warnung: " + e.getMessage());
            }
        });

        return builder.parse(xmlFile);
    }

    // SICHER: JSON-Parsing mit Schema-Validierung
    public Config parseJsonConfig(String jsonInput) throws ValidationException {
        // SICHER: JSON-Schema-Validierung verwenden
        ObjectMapper mapper = new ObjectMapper();

        // JSON parsen
        JsonNode rootNode;
        try {
            rootNode = mapper.readTree(jsonInput);
        } catch (JsonProcessingException e) {
            throw new ValidationException("Ungültige JSON-Syntax: " + e.getMessage());
        }

        // SICHER: Gegen Schema validieren
        JsonSchema schema = loadConfigSchema();
        Set<ValidationMessage> errors = schema.validate(rootNode);

        if (!errors.isEmpty()) {
            throw new ValidationException("JSON-Validierung fehlgeschlagen: " + errors);
        }

        // SICHER: In typisiertes Objekt deserialisieren
        return mapper.treeToValue(rootNode, Config.class);
    }
}
// SICHER: Protokoll-Parsing mit ordnungsgemäßer Syntaxvalidierung

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdbool.h>
#include <arpa/inet.h>

// SICHER: HTTP-Request-Parsing mit Validierung
bool secure_parse_http_request(const char* request,
                                char* method_out, size_t method_size,
                                char* path_out, size_t path_size,
                                char* version_out, size_t version_size) {
    const char* p = request;

    // SICHER: Methode parsen und validieren
    const char* valid_methods[] = {"GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS"};
    bool method_valid = false;

    size_t method_len = 0;
    while (*p && *p != ' ' && method_len < method_size - 1) {
        if (!isupper(*p)) {
            return false;  // Methode muss Großbuchstaben sein
        }
        method_out[method_len++] = *p++;
    }
    method_out[method_len] = '\0';

    // SICHER: Methode ist in erlaubter Liste
    for (int i = 0; i < sizeof(valid_methods)/sizeof(valid_methods[0]); i++) {
        if (strcmp(method_out, valid_methods[i]) == 0) {
            method_valid = true;
            break;
        }
    }
    if (!method_valid) {
        return false;
    }

    // Leerzeichen überspringen
    if (*p != ' ') return false;
    p++;

    // SICHER: Pfad parsen und validieren
    size_t path_len = 0;
    while (*p && *p != ' ' && path_len < path_size - 1) {
        // SICHER: Pfadzeichen validieren
        if (!isprint(*p) || *p == '<' || *p == '>') {
            return false;  // Ungültiges Pfadzeichen
        }
        path_out[path_len++] = *p++;
    }
    path_out[path_len] = '\0';

    // SICHER: Pfad muss mit / beginnen
    if (path_out[0] != '/') {
        return false;
    }

    // Leerzeichen überspringen
    if (*p != ' ') return false;
    p++;

    // SICHER: HTTP-Version parsen und validieren
    if (strncmp(p, "HTTP/", 5) != 0) {
        return false;
    }
    p += 5;

    // SICHER: Nur HTTP/1.0 oder HTTP/1.1 akzeptieren
    if (strcmp(p, "1.0\r\n") != 0 && strcmp(p, "1.1\r\n") != 0) {
        return false;
    }

    strncpy(version_out, p, version_size - 1);
    version_out[version_size - 1] = '\0';

    return true;
}

// SICHER: IP-Adress-Parsing mit strikter Formatvalidierung
bool secure_parse_ip(const char* ip_string, uint32_t* ip_out) {
    // SICHER: Nur Standard-Dezimal-Punkt-Notation akzeptieren
    unsigned int octets[4];
    char extra;

    // SICHER: Striktes Format-Parsing
    int parsed = sscanf(ip_string, "%u.%u.%u.%u%c",
                        &octets[0], &octets[1], &octets[2], &octets[3], &extra);

    // Muss genau 4 Oktette parsen, keine nachfolgenden Zeichen
    if (parsed != 4) {
        return false;
    }

    // SICHER: Oktett-Bereiche validieren
    for (int i = 0; i < 4; i++) {
        if (octets[i] > 255) {
            return false;
        }
    }

    // SICHER: Keine Oktal-/Hex-Notation (keine führenden Nullen außer bei 0)
    const char* p = ip_string;
    for (int i = 0; i < 4; i++) {
        if (*p == '0' && isdigit(*(p+1))) {
            // Führende Null - könnte oktal sein, ablehnen
            return false;
        }
        while (*p && *p != '.') p++;
        if (*p == '.') p++;
    }

    *ip_out = (octets[0] << 24) | (octets[1] << 16) | (octets[2] << 8) | octets[3];
    return true;
}

// SICHER: E-Mail-Validierung mit ordnungsgemäßer Syntaxprüfung
bool secure_validate_email(const char* email) {
    // SICHER: Umfassende E-Mail-Syntaxvalidierung
    const char* at = strchr(email, '@');
    if (at == NULL) return false;

    // Nur ein @ prüfen
    if (strchr(at + 1, '@') != NULL) return false;

    // Lokalen Teil validieren (vor @)
    const char* p = email;
    if (p == at) return false;  // Leerer lokaler Teil

    while (p < at) {
        char c = *p++;
        // SICHER: Erlaubte Zeichen im lokalen Teil validieren
        if (!isalnum(c) && c != '.' && c != '_' && c != '-' && c != '+') {
            return false;
        }
    }

    // Domain-Teil validieren (nach @)
    p = at + 1;
    if (*p == '\0') return false;  // Leere Domain

    int dot_count = 0;
    while (*p) {
        char c = *p++;
        if (c == '.') {
            dot_count++;
            if (*(p-2) == '.' || *(p) == '\0') {
                return false;  // Aufeinanderfolgende Punkte oder nachgestellter Punkt
            }
        } else if (!isalnum(c) && c != '-') {
            return false;
        }
    }

    // Mindestens ein Punkt in der Domain erforderlich
    if (dot_count == 0) return false;

    return true;
}
# SICHER: Eingabe-Parsing mit ordnungsgemäßer Syntaxvalidierung

import re
import json
from urllib.parse import urlparse
from datetime import datetime
from jsonschema import validate, ValidationError as JsonValidationError

# SICHER: URL-Parsing mit ordnungsgemäßer Validierung
def secure_parse_url(url):
    """URL-Syntax parsen und validieren."""
    # SICHER: Ordnungsgemäßen URL-Parser verwenden
    try:
        parsed = urlparse(url)
    except Exception as e:
        raise ValueError(f"Ungültige URL: {e}")

    # SICHER: Erforderliche Komponenten validieren
    if not parsed.scheme:
        raise ValueError("URL fehlt Schema")

    if parsed.scheme not in ('http', 'https'):
        raise ValueError(f"Ungültiges Schema: {parsed.scheme}")

    if not parsed.netloc:
        raise ValueError("URL fehlt Hostname")

    # SICHER: Hostname-Syntax validieren
    hostname = parsed.hostname
    if hostname is None:
        raise ValueError("Ungültiger Hostname")

    # Hostname-Zeichen prüfen
    hostname_pattern = r'^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*$'
    if not re.match(hostname_pattern, hostname):
        raise ValueError(f"Ungültige Hostname-Syntax: {hostname}")

    # SICHER: Port validieren falls vorhanden
    if parsed.port is not None:
        if parsed.port < 1 or parsed.port > 65535:
            raise ValueError(f"Ungültiger Port: {parsed.port}")

    return parsed

# SICHER: Datum-Parsing mit Formatvalidierung
def secure_parse_date(date_str, expected_format='%Y-%m-%d'):
    """Datum mit strikter Formatvalidierung parsen."""
    # SICHER: Formatmuster validieren
    if not isinstance(date_str, str):
        raise TypeError(f"Datum muss String sein, erhalten: {type(date_str)}")

    # SICHER: Länge muss erwartetem Format entsprechen
    if expected_format == '%Y-%m-%d' and len(date_str) != 10:
        raise ValueError(f"Ungültiges Datumsformat: {date_str}")

    # SICHER: Striktes datetime-Parsing verwenden
    try:
        parsed = datetime.strptime(date_str, expected_format)
    except ValueError as e:
        raise ValueError(f"Ungültiges Datum: {e}")

    # SICHER: Zusätzliche Validierung
    # Sicherstellen, dass geparstes Datum mit Eingabe übereinstimmt (fängt Probleme wie 30. Feb ab)
    if parsed.strftime(expected_format) != date_str:
        raise ValueError(f"Ungültige Datumswerte in: {date_str}")

    return {
        'year': parsed.year,
        'month': parsed.month,
        'day': parsed.day
    }

# SICHER: JSON-Konfigurationsvalidierung mit Schema
CONFIG_SCHEMA = {
    "type": "object",
    "required": ["serverUrl", "port"],
    "properties": {
        "serverUrl": {
            "type": "string",
            "pattern": "^https?://[a-zA-Z0-9.-]+(/.*)?$"
        },
        "port": {
            "type": "integer",
            "minimum": 1,
            "maximum": 65535
        },
        "timeout": {
            "type": "integer",
            "minimum": 0,
            "maximum": 300000
        }
    },
    "additionalProperties": False
}

def secure_parse_json_config(json_input):
    """JSON-Konfiguration mit Schema-Validierung parsen."""
    # SICHER: JSON parsen
    try:
        config = json.loads(json_input)
    except json.JSONDecodeError as e:
        raise ValueError(f"Ungültige JSON-Syntax: {e}")

    # SICHER: Gegen Schema validieren
    try:
        validate(instance=config, schema=CONFIG_SCHEMA)
    except JsonValidationError as e:
        raise ValueError(f"Konfigurationsvalidierung fehlgeschlagen: {e.message}")

    return config

CVE-Beispiele

  • CVE-2016-4029: Fehlerhafte IP-Adressformat-Validierung ermöglichte Umgehung durch Oktal-/Hex-Parsing - IP-Filter könnten mit alternativen Notationen umgangen werden.
  • CVE-2007-5893: Fehlende HTTP-Protokollversions-Validierung verursachte Anwendungsabstürze.

Verwandte CWEs

  • CWE-20: Unsachgemäße Eingabevalidierung (übergeordnet)
  • CWE-112: Fehlende XML-Validierung (untergeordnet)
  • CWE-1215: Datenvalidierungsprobleme (Kategorie)
  • CWE-91: XML Injection (verwandt)

Referenzen

  1. MITRE Corporation. "CWE-1286: Improper Validation of Syntactic Correctness of Input." https://cwe.mitre.org/data/definitions/1286.html
  2. OWASP. "Input Validation Cheat Sheet"
  3. W3C. "XML Schema Validation"