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
| Auswirkung | Details |
|---|---|
| Sonstiges | Bereich: Sonstiges Auswirkung variiert - von Abstürzen bis zu Sicherheitsumgehungen. |
| Integrität | Bereich: Integrität Injection-Angriffe können Daten oder Verhalten modifizieren. |
| Verfügbarkeit | Bereich: 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
- MITRE Corporation. "CWE-1286: Improper Validation of Syntactic Correctness of Input." https://cwe.mitre.org/data/definitions/1286.html
- OWASP. "Input Validation Cheat Sheet"
- W3C. "XML Schema Validation"