Unsachgemäße Validierung eines angegebenen Index, einer Position oder eines Offsets in der Eingabe

Beschreibung

Unsachgemäße Validierung eines angegebenen Index, einer Position oder eines Offsets in der Eingabe tritt auf, wenn ein Produkt Indizes, Positionen oder Offsets in Benutzereingaben nicht ordnungsgemäß validiert, bevor sie zum Zugriff auf indexierbare Ressourcen wie Puffer oder Dateien verwendet werden. Wenn nicht vertrauenswürdige Eingaben einen Index ohne Validierung angeben, können Angreifer auf nicht autorisierte Ressourcenbereiche zugreifen, was möglicherweise Buffer Overflows, Buffer Over-Reads, übermäßige Ressourcenallokation oder unerwartete Fehler verursacht.

Risiko

Unvalidierte Index-Eingaben haben schwerwiegende Sicherheitsauswirkungen. Buffer Overflows können auftreten. Out-of-Bounds-Lesezugriffe sind möglich. Informationsoffenlegung kann eintreten. Speicherbeschädigung kann auftreten. Denial-of-Service-Angriffe werden ermöglicht. Beliebige Codeausführung ist möglich. Sensible Daten können zugegriffen werden. Systemabstürze können auftreten.

Lösung

Implementieren Sie eine Eingabevalidierungsstrategie nach dem Prinzip "bekannt Gutes akzeptieren", die strikte Konformität mit Spezifikationen erfordert und nicht-konforme Eingaben ablehnt. Die Validierung sollte zulässige Wertebereiche, Grenzen gegen die tatsächliche Datengröße sowie Überlegungen zu vorzeichenbehafteten und vorzeichenlosen Werten berücksichtigen. Prüfen Sie Indizes immer gegen die tatsächlichen Array-/Puffergrenzen vor der Verwendung.

Häufige Auswirkungen

AuswirkungDetails
SonstigesBereich: Sonstiges

Auswirkung variiert je nach Kontext - von Abstürzen bis zu Datenlecks.
VertraulichkeitBereich: Vertraulichkeit

Speicher lesen - Out-of-Bounds-Lesen kann Daten offenlegen.
IntegritätBereich: Integrität

Speicher modifizieren - Out-of-Bounds-Schreiben kann Daten beschädigen.
VerfügbarkeitBereich: Verfügbarkeit

DoS - Ungültige Indizes können die Anwendung zum Absturz bringen.

Beispielcode und Lösung

Verwundbarer Code

// VERWUNDBAR: Keine Validierung des Array-Index

#include <stdint.h>
#include <stdlib.h>

#define MAX_MESSAGES 100

typedef struct {
    char subject[64];
    char body[1024];
    size_t size;
} message_t;

message_t messages[MAX_MESSAGES];
int message_count = 0;

// VERWUNDBAR: POP3-Server - keine Grenzprüfung der Nachrichtennummer
size_t vulnerable_get_message_size(int msg_num) {
    // VERWUNDBAR: Keine Validierung von msg_num
    // Benutzereingabe direkt als Index verwendet

    return messages[msg_num].size;

    // Wenn msg_num < 0 oder msg_num >= MAX_MESSAGES:
    // Out-of-Bounds-Lesen - undefiniertes Verhalten
    // Könnte sensible Daten aus angrenzendem Speicher lesen
}

// VERWUNDBAR: Pufferzugriff mit benutzergegebenem Offset
void vulnerable_read_buffer(const uint8_t* buffer, size_t buffer_size,
                            int offset) {
    // VERWUNDBAR: Keine Validierung des Offsets

    uint8_t value = buffer[offset];  // Out-of-Bounds wenn Offset ungültig
    process_byte(value);
}

// VERWUNDBAR: Benutzergesteuerter Array-Index
typedef struct {
    char name[32];
    double price;
    int quantity;
} product_t;

product_t products[1000];

product_t* vulnerable_get_product(int product_id) {
    // VERWUNDBAR: Keine Grenzprüfung
    // Benutzergegebene product_id direkt verwendet

    return &products[product_id];

    // Angreifer kann außerhalb des products-Arrays lesen/schreiben
}

// VERWUNDBAR: Datei-Seek mit Benutzer-Offset
void vulnerable_file_read(FILE* file, long offset, size_t length,
                          uint8_t* output) {
    // VERWUNDBAR: Keine Validierung des Offsets

    fseek(file, offset, SEEK_SET);  // Könnte über Dateiende hinaus suchen
    fread(output, 1, length, file);  // Undefiniertes Verhalten
}

// VERWUNDBAR: Negativer Index nicht geprüft
void vulnerable_negative_index(int* array, int index, int value) {
    // VERWUNDBAR: Negativer Index kann auf Speicher vor dem Array zugreifen

    array[index] = value;

    // array[-1] greift auf Speicher vor Array-Anfang zu
    // Kann angrenzende Datenstrukturen beschädigen
}
// VERWUNDBAR: Java mit unvalidierten Indizes

public class VulnerableProductService {

    private Product[] products = new Product[1000];

    // VERWUNDBAR: Keine Grenzprüfung des Index
    public Product getProduct(int index) {
        // VERWUNDBAR: ArrayIndexOutOfBoundsException oder unerwarteter Zugriff

        return products[index];
    }

    // VERWUNDBAR: Servlet mit benutzergegebenem Index
    public void handleRequest(HttpServletRequest request,
                              HttpServletResponse response) {
        // VERWUNDBAR: Benutzereingabe direkt als Index verwendet
        int productId = Integer.parseInt(request.getParameter("id"));

        Product product = products[productId];  // Keine Validierung!

        response.getWriter().write(product.toString());
    }

    // VERWUNDBAR: Listenzugriff ohne Grenzprüfung
    private List<String> messages = new ArrayList<>();

    public String getMessage(int index) {
        // VERWUNDBAR: Wirft Exception oder greift auf falsches Element zu

        return messages.get(index);
    }
}
# VERWUNDBAR: Python mit unvalidierten Indizes

messages = ["msg1", "msg2", "msg3"]
products = [{"name": "A", "price": 10}, {"name": "B", "price": 20}]

def vulnerable_get_message(index):
    # VERWUNDBAR: Keine Grenzprüfung
    # Negative Indizes in Python umbrechen!

    return messages[index]

    # index = -1 gibt letzte Nachricht zurück (möglicherweise nicht beabsichtigt)
    # index = 100 löst IndexError aus (DoS)

def vulnerable_slice_buffer(data, start, end):
    # VERWUNDBAR: Keine Validierung von start/end

    return data[start:end]

    # Negative Indizes verhalten sich unerwartet
    # Sehr große Indizes könnten Probleme verursachen

def vulnerable_dict_array_access(index):
    # VERWUNDBAR: Keine Grenzprüfung

    return products[index]

class VulnerableBuffer:
    def __init__(self, size):
        self.data = bytearray(size)

    def read_at(self, offset):
        # VERWUNDBAR: Keine Grenzprüfung
        return self.data[offset]

    def write_at(self, offset, value):
        # VERWUNDBAR: Keine Grenzprüfung
        self.data[offset] = value

Sichere Lösung

// SICHER: Ordnungsgemäße Validierung von Array-Indizes

#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>

#define MAX_MESSAGES 100

typedef struct {
    char subject[64];
    char body[1024];
    size_t size;
} message_t;

message_t messages[MAX_MESSAGES];
int message_count = 0;

// SICHER: Grenzprüfung der Nachrichtennummer
bool secure_get_message_size(int msg_num, size_t* size_out) {
    // SICHER: Indexbereich validieren
    if (msg_num < 0) {
        log_error("Ungültige Nachrichtennummer: %d (negativ)", msg_num);
        return false;
    }

    if (msg_num >= message_count) {
        log_error("Nachrichtennummer %d außerhalb des Bereichs (Anzahl=%d)",
                 msg_num, message_count);
        return false;
    }

    *size_out = messages[msg_num].size;
    return true;
}

// SICHER: Offset vor Pufferzugriff validieren
bool secure_read_buffer(const uint8_t* buffer, size_t buffer_size,
                        size_t offset, uint8_t* value_out) {
    // SICHER: Offset ist innerhalb der Grenzen
    if (offset >= buffer_size) {
        log_error("Offset %zu außerhalb der Grenzen (Größe=%zu)", offset, buffer_size);
        return false;
    }

    *value_out = buffer[offset];
    return true;
}

// SICHER: Array-Index mit Grenzen validieren
typedef struct {
    char name[32];
    double price;
    int quantity;
} product_t;

#define MAX_PRODUCTS 1000
product_t products[MAX_PRODUCTS];
int product_count = 0;

product_t* secure_get_product(int product_id) {
    // SICHER: Umfassende Validierung
    if (product_id < 0) {
        log_error("Ungültige Produkt-ID: %d (negativ)", product_id);
        return NULL;
    }

    if (product_id >= product_count) {
        log_error("Produkt-ID %d nicht gefunden (Anzahl=%d)",
                 product_id, product_count);
        return NULL;
    }

    return &products[product_id];
}

// SICHER: Datei-Offset validieren
bool secure_file_read(FILE* file, long offset, size_t length,
                      uint8_t* output, size_t output_size) {
    if (output == NULL || file == NULL) {
        return false;
    }

    // SICHER: Dateigröße zur Validierung ermitteln
    long current_pos = ftell(file);
    fseek(file, 0, SEEK_END);
    long file_size = ftell(file);
    fseek(file, current_pos, SEEK_SET);

    // SICHER: Offset validieren
    if (offset < 0) {
        log_error("Negativer Datei-Offset: %ld", offset);
        return false;
    }

    if (offset >= file_size) {
        log_error("Offset %ld über Dateigröße %ld hinaus", offset, file_size);
        return false;
    }

    // SICHER: Länge validieren
    if (length > output_size) {
        log_error("Länge %zu überschreitet Ausgabepuffer", length);
        return false;
    }

    if (offset + (long)length > file_size) {
        log_error("Lesen würde Dateigröße überschreiten");
        return false;
    }

    fseek(file, offset, SEEK_SET);
    size_t read = fread(output, 1, length, file);

    return (read == length);
}

// SICHER: Sicherer Array-Zugriff mit expliziter Größe
bool secure_array_write(int* array, size_t array_size,
                        int index, int value) {
    // SICHER: Auf negativen Index prüfen
    if (index < 0) {
        log_error("Negativer Index: %d", index);
        return false;
    }

    // SICHER: Gegen tatsächliche Größe prüfen
    if ((size_t)index >= array_size) {
        log_error("Index %d außerhalb der Grenzen (Größe=%zu)", index, array_size);
        return false;
    }

    array[index] = value;
    return true;
}

// SICHER: Hilfsfunktion für sicheren Array-Zugriff
#define SAFE_ARRAY_ACCESS(array, index, default_value) \
    ((index) >= 0 && (size_t)(index) < ARRAY_SIZE(array) ? \
     (array)[index] : (default_value))

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
// SICHER: Java mit validierten Indizes

public class SecureProductService {

    private Product[] products = new Product[1000];
    private int productCount = 0;

    // SICHER: Grenzprüfung des Index
    public Product getProduct(int index) throws ValidationException {
        // SICHER: Index validieren
        if (index < 0) {
            throw new ValidationException("Ungültiger Index: " + index + " (negativ)");
        }

        if (index >= productCount) {
            throw new ValidationException(
                "Produkt-Index " + index + " außerhalb des Bereichs (Anzahl=" + productCount + ")");
        }

        return products[index];
    }

    // SICHER: Servlet mit validiertem Index
    public void handleRequest(HttpServletRequest request,
                              HttpServletResponse response)
            throws IOException, ValidationException {

        String idParam = request.getParameter("id");

        // SICHER: Parameter-Existenz validieren
        if (idParam == null || idParam.isEmpty()) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Fehlender id-Parameter");
            return;
        }

        int productId;
        try {
            productId = Integer.parseInt(idParam);
        } catch (NumberFormatException e) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Ungültiges id-Format");
            return;
        }

        // SICHER: Validierten Getter verwenden
        try {
            Product product = getProduct(productId);
            response.getWriter().write(product.toString());
        } catch (ValidationException e) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
        }
    }

    // SICHER: Sicherer Listenzugriff
    private List<String> messages = new ArrayList<>();

    public Optional<String> getMessage(int index) {
        // SICHER: Grenzprüfung
        if (index < 0 || index >= messages.size()) {
            return Optional.empty();
        }

        return Optional.of(messages.get(index));
    }
}
# SICHER: Python mit validierten Indizes

from typing import Optional, List, Any

messages = ["msg1", "msg2", "msg3"]
products = [{"name": "A", "price": 10}, {"name": "B", "price": 20}]

def secure_get_message(index: int) -> Optional[str]:
    """Nachricht am Index mit Grenzprüfung abrufen."""
    # SICHER: Index-Typ validieren
    if not isinstance(index, int):
        raise TypeError(f"Index muss Integer sein, erhalten {type(index)}")

    # SICHER: Index-Bereich validieren
    if index < 0:
        raise ValueError(f"Index darf nicht negativ sein: {index}")

    if index >= len(messages):
        raise IndexError(f"Index {index} außerhalb des Bereichs (Größe={len(messages)})")

    return messages[index]

def secure_slice_buffer(data: bytes, start: int, end: int) -> bytes:
    """Puffer mit validierten Indizes schneiden."""
    # SICHER: Typen validieren
    if not isinstance(start, int) or not isinstance(end, int):
        raise TypeError("Start und Ende müssen Integer sein")

    # SICHER: Bereich validieren
    if start < 0:
        raise ValueError(f"Start darf nicht negativ sein: {start}")

    if end < start:
        raise ValueError(f"Ende {end} darf nicht kleiner als Start {start} sein")

    if end > len(data):
        raise ValueError(f"Ende {end} überschreitet Datenlänge {len(data)}")

    return data[start:end]

def secure_get_product(index: int) -> Optional[dict]:
    """Produkt am Index mit Validierung abrufen."""
    # SICHER: Umfassende Validierung
    if not isinstance(index, int):
        raise TypeError(f"Index muss Integer sein, erhalten {type(index)}")

    if index < 0:
        return None  # Oder ValueError auslösen

    if index >= len(products):
        return None  # Oder IndexError auslösen

    return products[index]

class SecureBuffer:
    def __init__(self, size: int):
        if size <= 0:
            raise ValueError(f"Größe muss positiv sein: {size}")
        self.data = bytearray(size)

    def read_at(self, offset: int) -> Optional[int]:
        """Byte am Offset mit Grenzprüfung lesen."""
        # SICHER: Offset validieren
        if not isinstance(offset, int):
            raise TypeError(f"Offset muss Integer sein, erhalten {type(offset)}")

        if offset < 0:
            raise ValueError(f"Offset darf nicht negativ sein: {offset}")

        if offset >= len(self.data):
            raise IndexError(f"Offset {offset} außerhalb der Grenzen (Größe={len(self.data)})")

        return self.data[offset]

    def write_at(self, offset: int, value: int) -> bool:
        """Byte am Offset mit Grenzprüfung schreiben."""
        # SICHER: Offset validieren
        if not isinstance(offset, int):
            raise TypeError(f"Offset muss Integer sein")

        if offset < 0 or offset >= len(self.data):
            return False

        # SICHER: Wert validieren
        if not isinstance(value, int) or value < 0 or value > 255:
            raise ValueError(f"Wert muss Byte sein (0-255): {value}")

        self.data[offset] = value
        return True

CVE-Beispiele

  • CVE-2005-0369: Große Paket-ID als Array-Index in Netzwerkprotokoll verwendet.
  • CVE-2001-1009: Negativer Array-Index im POP-LIST-Befehl verursachte Out-of-Bounds-Zugriff.

Verwandte CWEs

  • CWE-20: Unsachgemäße Eingabevalidierung (übergeordnet)
  • CWE-129: Unsachgemäße Validierung des Array-Index (untergeordnet)
  • CWE-781: Unsachgemäße Adressvalidierung in IOCTL (untergeordnet)
  • CWE-787: Out-of-Bounds-Schreiben (verwandt)
  • CWE-125: Out-of-Bounds-Lesen (verwandt)

Referenzen

  1. MITRE Corporation. "CWE-1285: Improper Validation of Specified Index, Position, or Offset in Input." https://cwe.mitre.org/data/definitions/1285.html
  2. CERT C. "ARR30-C: Do not form or use out-of-bounds pointers or array subscripts"
  3. OWASP. "Input Validation Cheat Sheet"