Unsachgemäße Validierung der Konsistenz innerhalb der Eingabe
Beschreibung
Unsachgemäße Validierung der Konsistenz innerhalb der Eingabe tritt auf, wenn ein Produkt Eingaben mit mehreren Elementen oder Feldern erhält, die miteinander übereinstimmen sollten, aber diese Konsistenz nicht validiert oder falsch validiert. Komplexe strukturierte Eingaben enthalten oft voneinander abhängige Felder -- wie ein Zählfeld gefolgt von der angegebenen Anzahl von Elementen, oder ein Größenfeld, das mit der tatsächlichen Datenlänge übereinstimmen muss. Wenn diese Eingaben inkonsistent sind, können Angreifer unerwartete Fehler auslösen, falsche Aktionen verursachen oder ruhende Schwachstellen ausnutzen.
Risiko
Inkonsistente Eingabevalidierung hat schwerwiegende Sicherheitsauswirkungen. Buffer Overflows durch Größenabweichungen. Datenbeschädigung durch Zählfehler. Denial of Service möglich. Logikfehler können auftreten. Speicherbeschädigungsangriffe werden ermöglicht. Sicherheitsumgehungen möglich. Parsing-Schwachstellen aufgedeckt. Anwendungsabstürze wahrscheinlich.
Lösung
Implementieren Sie eine Allowlist-basierte Validierung, die strikte Konformität mit Spezifikationen erfordert. Validieren Sie alle relevanten Eigenschaften: Länge, Typ, Wertebereiche, Syntax und Konsistenz zwischen Feldern. Lehnen Sie Eingaben ab, die die Validierung nicht bestehen, anstatt Reparaturen zu versuchen. Überprüfen Sie, dass Zählfelder mit der tatsächlichen Elementanzahl übereinstimmen. Stellen Sie sicher, dass Größenfelder mit der tatsächlichen Datengröße übereinstimmen. Kreuzvalidieren Sie verwandte Felder.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Sonstiges | Bereich: Sonstiges Auswirkung variiert je nachdem, wie inkonsistente Eingaben verarbeitet werden. |
| Integrität | Bereich: Integrität Inkonsistente Daten können den Anwendungszustand beschädigen. |
| Verfügbarkeit | Bereich: Verfügbarkeit Nicht übereinstimmende Zähler/Größen können Abstürze verursachen. |
Beispielcode und Lösung
Verwundbarer Code
// VERWUNDBAR: Keine Konsistenzvalidierung zwischen Feldern
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
uint32_t count;
uint32_t items[100]; // Festes Maximum, aber 'count' könnte größer sein
} vulnerable_packet_t;
// VERWUNDBAR: Zähler nicht gegen tatsächliche Elemente validiert
void vulnerable_process_packet(const uint8_t* data, size_t data_len) {
// VERWUNDBAR: Zähler ohne Validierung lesen
uint32_t count;
memcpy(&count, data, sizeof(count));
// VERWUNDBAR: Keine Prüfung, ob count mit verfügbaren Daten übereinstimmt
// Wenn count > (data_len - 4) / 4, lesen wir über den Puffer hinaus
for (uint32_t i = 0; i < count; i++) {
uint32_t item;
memcpy(&item, data + 4 + i * 4, sizeof(item));
process_item(item); // Buffer Over-Read wenn count zu groß
}
}
// VERWUNDBAR: Größenfeld nicht gegen Paketgröße validiert
typedef struct {
uint32_t total_size;
uint32_t header_size;
uint32_t data_size;
uint8_t header[256];
uint8_t data[1024];
} message_t;
void vulnerable_process_message(const message_t* msg, size_t actual_size) {
// VERWUNDBAR: Keine Validierung der Größenfelder
// total_size könnte inkonsistent mit actual_size sein
// header_size + data_size könnte != total_size sein
// VERWUNDBAR: Bereitgestellte Größen ohne Validierung verwenden
process_header(msg->header, msg->header_size);
process_data(msg->data, msg->data_size);
}
// VERWUNDBAR: Start-/Endblock-Inkonsistenz
typedef struct {
uint32_t start_block;
uint32_t end_block;
uint8_t block_data[4096];
} block_range_t;
void vulnerable_process_blocks(const block_range_t* range) {
// VERWUNDBAR: Keine Prüfung, ob start < end
// Wenn start > end, könnte die Schleife unterlaufen oder falsch überspringen
for (uint32_t i = range->start_block; i <= range->end_block; i++) {
// Wenn start=100, end=50, wird dies nie ausgeführt (oder umgebrochen)
process_block(i, range->block_data);
}
}
# VERWUNDBAR: Python mit inkonsistenter Feldbehandlung
import struct
# VERWUNDBAR: Längenfeld nicht validiert
def vulnerable_parse_tlv(data):
"""Type-Length-Value-Struktur parsen."""
offset = 0
results = []
while offset < len(data):
# Typ und Länge lesen
tlv_type = data[offset]
length = struct.unpack('>H', data[offset+1:offset+3])[0]
# VERWUNDBAR: Keine Prüfung, ob Länge mit verbleibenden Daten konsistent ist
value = data[offset+3:offset+3+length]
# Wenn length > verbleibende Daten, wird value stillschweigend gekürzt
results.append({'type': tlv_type, 'length': length, 'value': value})
offset += 3 + length
return results
# VERWUNDBAR: Array-Zähler nicht validiert
def vulnerable_process_items(data):
"""Elemente mit Zähler-Präfix verarbeiten."""
count = struct.unpack('>I', data[:4])[0]
# VERWUNDBAR: Keine Validierung, dass Daten 'count' Elemente enthalten
items = []
offset = 4
for i in range(count):
# Wirft IndexError wenn nicht genug Daten vorhanden
# Oder liest teilweise/falsche Daten
item_size = struct.unpack('>I', data[offset:offset+4])[0]
item_data = data[offset+4:offset+4+item_size]
items.append(item_data)
offset += 4 + item_size
return items
# VERWUNDBAR: Prüfsumme nicht validiert
def vulnerable_process_packet(packet):
"""Paket mit Prüfsumme verarbeiten."""
data = packet['data']
checksum = packet['checksum']
# VERWUNDBAR: Prüfsumme nicht verifiziert
# Angreifer kann Daten und Prüfsumme unabhängig modifizieren
process_data(data) # Potenziell beschädigte Daten verarbeiten
// VERWUNDBAR: Java mit inkonsistenter Eingabebehandlung
public class VulnerableParser {
// VERWUNDBAR: Deklarierte Länge nicht gegen tatsächliche validiert
public void processChunk(byte[] data) {
int offset = 0;
while (offset < data.length) {
// Deklarierte Chunk-Größe lesen
int chunkSize = ByteBuffer.wrap(data, offset, 4).getInt();
offset += 4;
// VERWUNDBAR: Keine Validierung, dass chunkSize Bytes verfügbar sind
byte[] chunk = new byte[chunkSize];
// ArrayIndexOutOfBoundsException oder liest falsche Daten
System.arraycopy(data, offset, chunk, 0, chunkSize);
offset += chunkSize;
processChunkData(chunk);
}
}
// VERWUNDBAR: Zählerfeld-Inkonsistenz
public List<String> parseStringList(DataInputStream input) throws IOException {
int count = input.readInt(); // Deklarierter Zähler
List<String> result = new ArrayList<>();
// VERWUNDBAR: Dem Zählerfeld vertrauen
for (int i = 0; i < count; i++) {
int length = input.readInt();
byte[] bytes = new byte[length];
// VERWUNDBAR: Keine Validierung der Länge gegen verfügbare Daten
input.readFully(bytes);
result.add(new String(bytes, StandardCharsets.UTF_8));
}
return result;
}
}
Sichere Lösung
// SICHER: Ordnungsgemäße Konsistenzvalidierung
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
// SICHER: Zähler gegen verfügbare Daten validieren
bool secure_process_packet(const uint8_t* data, size_t data_len) {
// SICHER: Mindestgrößenprüfung
if (data_len < sizeof(uint32_t)) {
log_error("Paket zu klein für Zählerfeld");
return false;
}
// Zähler lesen
uint32_t count;
memcpy(&count, data, sizeof(count));
// SICHER: Zähler gegen tatsächliche Daten validieren
size_t expected_size = sizeof(uint32_t) + count * sizeof(uint32_t);
if (expected_size > data_len) {
log_error("Zähler %u überschreitet verfügbare Daten (haben %zu, brauchen %zu)",
count, data_len, expected_size);
return false;
}
// SICHER: Auch auf Zählerüberlauf prüfen
if (count > (SIZE_MAX - sizeof(uint32_t)) / sizeof(uint32_t)) {
log_error("Zählerüberlauf");
return false;
}
// Jetzt sicher zu verarbeiten
for (uint32_t i = 0; i < count; i++) {
uint32_t item;
memcpy(&item, data + 4 + i * 4, sizeof(item));
process_item(item);
}
return true;
}
// SICHER: Alle Größenfelder auf Konsistenz validieren
typedef struct {
uint32_t total_size;
uint32_t header_size;
uint32_t data_size;
uint8_t header[256];
uint8_t data[1024];
} message_t;
bool secure_process_message(const message_t* msg, size_t actual_size) {
// SICHER: actual_size ist mindestens Minimum
if (actual_size < offsetof(message_t, header)) {
log_error("Nachricht zu klein");
return false;
}
// SICHER: total_size stimmt mit actual_size überein
if (msg->total_size != actual_size) {
log_error("Gesamtgröße stimmt nicht überein: deklariert %u, tatsächlich %zu",
msg->total_size, actual_size);
return false;
}
// SICHER: header_size validieren
if (msg->header_size > sizeof(msg->header)) {
log_error("Header-Größe überschreitet Puffer");
return false;
}
// SICHER: data_size validieren
if (msg->data_size > sizeof(msg->data)) {
log_error("Datengröße überschreitet Puffer");
return false;
}
// SICHER: Größen sind konsistent
size_t computed_size = offsetof(message_t, header) +
msg->header_size + msg->data_size;
if (computed_size != msg->total_size) {
log_error("Größenfelder inkonsistent: header=%u, data=%u, total=%u",
msg->header_size, msg->data_size, msg->total_size);
return false;
}
// Jetzt sicher zu verarbeiten
process_header(msg->header, msg->header_size);
process_data(msg->data, msg->data_size);
return true;
}
// SICHER: Start-/End-Konsistenz validieren
typedef struct {
uint32_t start_block;
uint32_t end_block;
uint8_t block_data[4096];
} block_range_t;
bool secure_process_blocks(const block_range_t* range) {
// SICHER: start <= end validieren
if (range->start_block > range->end_block) {
log_error("Ungültiger Blockbereich: start %u > end %u",
range->start_block, range->end_block);
return false;
}
// SICHER: Bereich ist angemessen
uint32_t count = range->end_block - range->start_block + 1;
if (count > MAX_BLOCKS) {
log_error("Blockbereich zu groß: %u Blöcke", count);
return false;
}
for (uint32_t i = range->start_block; i <= range->end_block; i++) {
process_block(i, range->block_data);
}
return true;
}
# SICHER: Python mit Konsistenzvalidierung
import struct
from typing import List, Dict, Any, Optional
# SICHER: TLV-Parsing mit Längenvalidierung
def secure_parse_tlv(data: bytes) -> List[Dict[str, Any]]:
"""Type-Length-Value-Struktur mit Validierung parsen."""
offset = 0
results = []
while offset < len(data):
# SICHER: Minimale Header-Größe prüfen
if offset + 3 > len(data):
raise ValueError(f"Unvollständiger TLV-Header bei Offset {offset}")
# Typ und Länge lesen
tlv_type = data[offset]
length = struct.unpack('>H', data[offset+1:offset+3])[0]
# SICHER: Länge gegen verbleibende Daten validieren
if offset + 3 + length > len(data):
raise ValueError(
f"TLV-Länge {length} überschreitet verbleibende Daten "
f"(offset={offset}, data_len={len(data)})"
)
value = data[offset+3:offset+3+length]
# SICHER: Erwartete Menge erhalten
if len(value) != length:
raise ValueError(f"TLV-Wert gekürzt: erwartet {length}, erhalten {len(value)}")
results.append({'type': tlv_type, 'length': length, 'value': value})
offset += 3 + length
return results
# SICHER: Array-Zähler-Validierung
def secure_process_items(data: bytes, max_count: int = 1000) -> List[bytes]:
"""Elemente mit validiertem Zähler-Präfix verarbeiten."""
if len(data) < 4:
raise ValueError("Daten zu kurz für Zählerfeld")
count = struct.unpack('>I', data[:4])[0]
# SICHER: Zähler ist angemessen
if count > max_count:
raise ValueError(f"Zähler {count} überschreitet Maximum {max_count}")
items = []
offset = 4
for i in range(count):
# SICHER: Element-Header prüfen
if offset + 4 > len(data):
raise ValueError(f"Unvollständiger Element-Header bei Index {i}")
item_size = struct.unpack('>I', data[offset:offset+4])[0]
# SICHER: item_size gegen verbleibende Daten validieren
if offset + 4 + item_size > len(data):
raise ValueError(
f"Element {i} Größe {item_size} überschreitet verbleibende Daten"
)
item_data = data[offset+4:offset+4+item_size]
items.append(item_data)
offset += 4 + item_size
# SICHER: Alle Daten verbraucht (kein nachfolgendes Müll)
if offset != len(data):
raise ValueError(f"Unerwartete nachfolgende Daten: {len(data) - offset} Bytes")
return items
# SICHER: Prüfsummenvalidierung
import hashlib
def secure_process_packet(packet: Dict[str, Any]) -> bool:
"""Paket mit Prüfsummenvalidierung verarbeiten."""
if 'data' not in packet or 'checksum' not in packet:
raise ValueError("Fehlende erforderliche Felder")
data = packet['data']
expected_checksum = packet['checksum']
# SICHER: Prüfsumme vor Verarbeitung verifizieren
actual_checksum = hashlib.sha256(data).hexdigest()
if actual_checksum != expected_checksum:
raise ValueError(
f"Prüfsumme stimmt nicht überein: erwartet {expected_checksum}, "
f"berechnet {actual_checksum}"
)
# Jetzt sicher, verifizierte Daten zu verarbeiten
process_data(data)
return True
// SICHER: Java mit Konsistenzvalidierung
public class SecureParser {
private static final int MAX_CHUNK_SIZE = 1024 * 1024; // 1 MB
// SICHER: Deklarierte Länge gegen tatsächliche validieren
public void processChunk(byte[] data) throws ValidationException {
int offset = 0;
while (offset < data.length) {
// SICHER: Header passt
if (offset + 4 > data.length) {
throw new ValidationException(
"Unvollständiger Chunk-Header bei Offset " + offset);
}
// Deklarierte Chunk-Größe lesen
int chunkSize = ByteBuffer.wrap(data, offset, 4).getInt();
offset += 4;
// SICHER: Chunk-Größe validieren
if (chunkSize < 0) {
throw new ValidationException("Negative Chunk-Größe: " + chunkSize);
}
if (chunkSize > MAX_CHUNK_SIZE) {
throw new ValidationException(
"Chunk-Größe " + chunkSize + " überschreitet Maximum " + MAX_CHUNK_SIZE);
}
// SICHER: Chunk-Daten verfügbar
if (offset + chunkSize > data.length) {
throw new ValidationException(
"Chunk-Größe " + chunkSize + " überschreitet verbleibende Daten " +
(data.length - offset));
}
byte[] chunk = new byte[chunkSize];
System.arraycopy(data, offset, chunk, 0, chunkSize);
offset += chunkSize;
processChunkData(chunk);
}
}
// SICHER: Zählerfeld-Validierung
public List<String> parseStringList(DataInputStream input, int maxCount)
throws IOException, ValidationException {
int count = input.readInt();
// SICHER: Zähler validieren
if (count < 0) {
throw new ValidationException("Negativer Zähler: " + count);
}
if (count > maxCount) {
throw new ValidationException(
"Zähler " + count + " überschreitet Maximum " + maxCount);
}
List<String> result = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
// SICHER: Zeichenkettenlänge validieren
int length = input.readInt();
if (length < 0) {
throw new ValidationException(
"Negative Zeichenkettenlänge bei Index " + i);
}
if (length > MAX_STRING_LENGTH) {
throw new ValidationException(
"Zeichenkettenlänge " + length + " überschreitet Maximum bei Index " + i);
}
byte[] bytes = new byte[length];
// SICHER: Erwartete Menge gelesen
int read = input.read(bytes);
if (read != length) {
throw new ValidationException(
"Erwartet " + length + " Bytes, erhalten " + read);
}
result.add(new String(bytes, StandardCharsets.UTF_8));
}
return result;
}
}
CVE-Beispiele
- CVE-2018-16733: Produkt validiert nicht, dass der Startblock vor dem Endblock erscheint, was Verarbeitungsfehler verursacht.
- CVE-2006-3790: Größenfeld, das inkonsistent mit der Paketgröße ist, führt zu Buffer Over-Read.
- CVE-2008-4114: Systemabsturz durch Offset-Wert, der inkonsistent mit der Paketgröße ist.
Verwandte CWEs
- CWE-20: Unsachgemäße Eingabevalidierung (übergeordnet)
- CWE-1215: Datenvalidierungsprobleme (Kategorie)
- CWE-130: Unsachgemäße Behandlung von Längenparameter-Inkonsistenz (verwandt)
- CWE-805: Pufferzugriff mit falschem Längenwert (verwandt)
Referenzen
- MITRE Corporation. "CWE-1288: Improper Validation of Consistency within Input." https://cwe.mitre.org/data/definitions/1288.html
- OWASP. "Input Validation Cheat Sheet"
- CERT. "Secure Coding Guidelines"