Fehlender Handler

Beschreibung

Fehlender Handler ist eine Schwachstelle, bei der ein Handler für eine bestimmte Bedingung, ein Ereignis oder eine Ausnahme nicht verfügbar oder implementiert ist. Wenn eine Ausnahme geworfen und nicht abgefangen wird, oder wenn ein Ereignis ohne entsprechenden Handler auftritt, hat der Prozess die Gelegenheit aufgegeben zu entscheiden, ob ein bestimmter Fehler oder ein Ereignis eine Änderung der Ausführung wert ist. Dies kann zu Abstürzen, Informationsoffenlegung durch Fehlermeldungen oder undefiniertem Verhalten führen, wenn die Anwendung auf unerwartete Situationen ohne ordnungsgemäße Behandlungsmechanismen trifft.

Risiko

Fehlende Handler können zu Anwendungsabstürzen, Denial of Service und Informationsoffenlegung führen. Nicht abgefangene Ausnahmen offenbaren oft Debug-Informationen, Stack-Traces und interne Implementierungsdetails, die für Angreifer wertvoll sind. In vernetzten Anwendungen können fehlende Handler für fehlerhafte Eingaben Dienste zum Absturz bringen oder sie in undefinierte Zustände versetzen. Anwendungen ohne Handler für Ressourcenerschöpfung können ungraceful versagen. Sicherheitskritische Operationen ohne Fehler-Handler können Systeme in unsicheren Zuständen hinterlassen. Das unvorhersehbare Verhalten unbehandelter Ereignisse macht Anwendungen unzuverlässig und potenziell ausnutzbar.

Lösung

Behandeln Sie alle möglichen Situationen einschließlich Fehlerbedingungen umfassend. Wenn eine Operation eine Ausnahme werfen kann, implementieren Sie einen Handler für diesen spezifischen Ausnahmetyp. Verwenden Sie Catch-All-Handler als letzten Ausweg, um nicht abgefangene Ausnahmen zu verhindern, während Sie die unerwartete Bedingung trotzdem protokollieren. Implementieren Sie Standard-Handler für Ereignisse, die möglicherweise keine spezifischen Handler haben. Entwerfen Sie APIs so, dass explizite Behandlung von Fehlerfällen erforderlich ist. Verwenden Sie statische Analysetools, um unbehandelte Ausnahmen zu erkennen. Testen Sie mit Fuzzing, um unbehandelte Randfälle zu entdecken.

Häufige Auswirkungen

AuswirkungDetails
SonstigesUmfang: Sonstiges

Variiert je nach Kontext - Die Auswirkung hängt vom spezifischen Implementierungskontext ab. Nicht abgefangene Ausnahmen können Abstürze, Informationsoffenlegung durch Fehlermeldungen oder undefinierte Anwendungszustände verursachen.

Beispielcode

Anfälliger Code

// Anfällig: Servlet ohne Ausnahmebehandlung
public class VulnerableServlet extends HttpServlet {

    protected void doPost(HttpServletRequest req, HttpServletResponse res)
            throws IOException {
        String ip = req.getRemoteAddr();

        // Anfällig: DNS-Lookup kann mit UnknownHostException fehlschlagen
        // Kein Handler - Ausnahme propagiert, offenbart Debug-Infos
        InetAddress addr = InetAddress.getByName(ip);

        PrintWriter out = res.getWriter();
        out.println("hello " + addr.getHostName());
    }
}

// Wenn DNS-Lookup fehlschlägt, sieht Angreifer:
// java.net.UnknownHostException: 192.168.1.100
//     at java.net.InetAddress.getByName(InetAddress.java:...)
//     at VulnerableServlet.doPost(VulnerableServlet.java:10)
//     ...
# Anfällig: Fehlende Handler für Dateioperationen
def vulnerable_read_config(config_path):
    # Anfällig: Kein Handler für FileNotFoundError
    with open(config_path, 'r') as f:
        config = json.load(f)  # Kein Handler für JSONDecodeError

    return config

# Anfällig: Fehlender Handler für Netzwerkoperationen
def vulnerable_fetch_data(url):
    # Anfällig: Kein Handler für Verbindungsfehler, Timeouts, etc.
    response = requests.get(url)
    return response.json()  # Kein Handler für ungültiges JSON
// Anfällig: Fehlender Handler für Speicherallokationsfehler
#include <stdlib.h>

void vulnerable_process_data(size_t size) {
    // Anfällig: malloc kann NULL zurückgeben
    char *buffer = malloc(size);

    // Anfällig: Keine NULL-Prüfung - stürzt bei Dereferenzierung ab
    memset(buffer, 0, size);

    process(buffer);
    free(buffer);
}

// Anfällig: Fehlender Handler für Signal
void vulnerable_server() {
    // Anfällig: Kein SIGPIPE-Handler - Schreiben auf geschlossenen Socket stürzt ab
    while (1) {
        int client = accept(server_fd, NULL, NULL);
        handle_client(client);
    }
}
// Anfällig: Fehlende Handler in ereignisgesteuertem Code
public class VulnerableEventHandler
{
    public void ProcessMessage(string message)
    {
        // Anfällig: Kein Handler für null-Nachricht
        var parsed = ParseMessage(message);  // NullReferenceException

        // Anfällig: Kein Handler für ungültiges Format
        var data = ExtractData(parsed);  // FormatException

        // Anfällig: Kein Handler für DB-Fehler
        SaveToDatabase(data);  // SqlException
    }

    // Anfällig: Fehlender Event-Handler
    public void Initialize()
    {
        connection.OnDisconnect += null;  // Kein Handler für Disconnect-Ereignisse
        // Anwendung weiß nicht, wenn Verbindung verloren geht
    }
}

Korrigierter Code

// Korrigiert: Umfassende Ausnahmebehandlung
public class SecureServlet extends HttpServlet {

    private static final Logger logger = LoggerFactory.getLogger(SecureServlet.class);

    protected void doPost(HttpServletRequest req, HttpServletResponse res)
            throws IOException {
        String ip = req.getRemoteAddr();
        PrintWriter out = res.getWriter();

        try {
            // Korrigiert: Potenziellen DNS-Fehler behandeln
            InetAddress addr = InetAddress.getByName(ip);
            out.println("hello " + addr.getHostName());

        } catch (UnknownHostException e) {
            // Korrigiert: Fehler intern protokollieren, sichere Nachricht an Benutzer zeigen
            logger.warn("DNS-Lookup fehlgeschlagen für IP: {}", ip, e);
            out.println("hello " + ip);  // Fallback auf IP-Adresse

        } catch (SecurityException e) {
            // Korrigiert: Security-Manager-Einschränkungen behandeln
            logger.error("Security-Ausnahme während DNS-Lookup", e);
            res.sendError(HttpServletResponse.SC_FORBIDDEN, "Zugriff verweigert");

        } catch (Exception e) {
            // Korrigiert: Catch-All für unerwartete Ausnahmen
            logger.error("Unerwarteter Fehler in doPost", e);
            res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                         "Ein Fehler ist aufgetreten");
        }
    }
}
# Korrigiert: Umfassende Fehlerbehandlung
import json
import logging
import requests
from requests.exceptions import RequestException, Timeout, ConnectionError

logger = logging.getLogger(__name__)

def secure_read_config(config_path, default_config=None):
    try:
        with open(config_path, 'r') as f:
            config = json.load(f)
        return config

    except FileNotFoundError:
        # Korrigiert: Fehlende Datei behandeln
        logger.warning(f"Config-Datei nicht gefunden: {config_path}")
        if default_config:
            return default_config
        raise ConfigurationError(f"Erforderliche Config-Datei fehlt: {config_path}")

    except json.JSONDecodeError as e:
        # Korrigiert: Fehlerhaftes JSON behandeln
        logger.error(f"Ungültiges JSON in Config-Datei: {e}")
        raise ConfigurationError(f"Fehlerhafte Config-Datei: {config_path}")

    except PermissionError:
        # Korrigiert: Berechtigungsprobleme behandeln
        logger.error(f"Berechtigung zum Lesen der Config verweigert: {config_path}")
        raise ConfigurationError(f"Config-Datei kann nicht gelesen werden: {config_path}")


def secure_fetch_data(url, timeout=30, retries=3):
    for attempt in range(retries):
        try:
            response = requests.get(url, timeout=timeout)
            response.raise_for_status()  # Bei 4xx/5xx werfen
            return response.json()

        except Timeout:
            # Korrigiert: Timeout behandeln
            logger.warning(f"Anfrage-Timeout (Versuch {attempt + 1}/{retries}): {url}")
            if attempt == retries - 1:
                raise

        except ConnectionError as e:
            # Korrigiert: Verbindungsfehler behandeln
            logger.error(f"Verbindung fehlgeschlagen: {url} - {e}")
            raise

        except requests.HTTPError as e:
            # Korrigiert: HTTP-Fehler behandeln
            logger.error(f"HTTP-Fehler {e.response.status_code}: {url}")
            raise

        except json.JSONDecodeError:
            # Korrigiert: Ungültige JSON-Antwort behandeln
            logger.error(f"Ungültige JSON-Antwort von: {url}")
            raise DataError(f"Server gab ungültiges JSON zurück")
// Korrigiert: Ordnungsgemäße Fehlerbehandlung für alle Fehlermodi
#include <stdlib.h>
#include <signal.h>
#include <errno.h>

// Korrigiert: Handler für SIGPIPE
void sigpipe_handler(int sig) {
    // Protokollieren aber nicht abstürzen - write() EPIPE zurückgeben lassen
}

int secure_process_data(size_t size) {
    // Korrigiert: Größe validieren
    if (size == 0 || size > MAX_ALLOWED_SIZE) {
        log_error("Ungültige Größe: %zu", size);
        return -1;
    }

    // Korrigiert: malloc-Rückgabewert prüfen
    char *buffer = malloc(size);
    if (buffer == NULL) {
        log_error("Speicherallokation fehlgeschlagen für Größe: %zu", size);
        return -1;
    }

    memset(buffer, 0, size);

    int result = process(buffer);

    free(buffer);
    return result;
}

int secure_server() {
    // Korrigiert: Signal-Handler installieren
    struct sigaction sa;
    sa.sa_handler = sigpipe_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sigaction(SIGPIPE, &sa, NULL);

    while (1) {
        int client = accept(server_fd, NULL, NULL);

        // Korrigiert: accept-Fehler behandeln
        if (client < 0) {
            if (errno == EINTR) {
                continue;  // Unterbrochen, erneut versuchen
            }
            log_error("Accept fehlgeschlagen: %s", strerror(errno));
            continue;  // Nicht abstürzen, weiter akzeptieren
        }

        // Korrigiert: Client in try-catch-Äquivalent behandeln
        if (handle_client(client) < 0) {
            log_error("Client-Behandlung fehlgeschlagen");
        }

        close(client);
    }
}
// Korrigiert: Umfassende Event- und Ausnahmebehandlung
public class SecureEventHandler
{
    private readonly ILogger _logger;

    public void ProcessMessage(string message)
    {
        // Korrigiert: Eingabe validieren
        if (string.IsNullOrEmpty(message))
        {
            _logger.LogWarning("Null- oder leere Nachricht empfangen");
            return;
        }

        try
        {
            var parsed = ParseMessage(message);
            var data = ExtractData(parsed);
            SaveToDatabase(data);
        }
        catch (FormatException ex)
        {
            // Korrigiert: Ungültiges Format behandeln
            _logger.LogError(ex, "Ungültiges Nachrichtenformat");
            throw new MessageProcessingException("Ungültiges Nachrichtenformat", ex);
        }
        catch (SqlException ex)
        {
            // Korrigiert: Datenbankfehler behandeln
            _logger.LogError(ex, "Datenbankfehler beim Speichern der Nachricht");
            throw new MessageProcessingException("Nachricht könnte nicht gespeichert werden", ex);
        }
        catch (Exception ex)
        {
            // Korrigiert: Catch-All mit Protokollierung
            _logger.LogError(ex, "Unerwarteter Fehler bei Nachrichtenverarbeitung");
            throw;
        }
    }

    public void Initialize()
    {
        // Korrigiert: Event-Handler installieren
        connection.OnDisconnect += HandleDisconnect;
        connection.OnError += HandleError;
        connection.OnTimeout += HandleTimeout;
    }

    private void HandleDisconnect(object sender, EventArgs e)
    {
        _logger.LogWarning("Verbindung getrennt");
        // Wiederverbindung versuchen oder aufräumen
        TryReconnect();
    }

    private void HandleError(object sender, ErrorEventArgs e)
    {
        _logger.LogError(e.Exception, "Verbindungsfehler");
    }

    private void HandleTimeout(object sender, EventArgs e)
    {
        _logger.LogWarning("Verbindungs-Timeout");
    }
}

CVE-Beispiele

  • CVE-2022-25302 — SDK für OPC Unified Architecture (OPC UA) fehlt ein Handler für fehlgeschlagene Casts, was einen Absturz ermöglicht.

Referenzen

  1. MITRE Corporation. "CWE-431: Missing Handler." https://cwe.mitre.org/data/definitions/431.html
  2. CERT Oracle Secure Coding Standard for Java. "ERR00-J. Do not suppress or ignore checked exceptions." https://wiki.sei.cmu.edu/confluence/display/java/ERR00-J.+Do+not+suppress+or+ignore+checked+exceptions