Erkennung einer Fehlerbedingung ohne Aktion

Beschreibung

Erkennung einer Fehlerbedingung ohne Aktion ist eine Schwachstelle, die auftritt, wenn Software einen bestimmten Fehler erkennt, aber keine Maßnahmen zur Behandlung ergreift. Dies umfasst leere Catch-Blöcke, ignorierte Rückgabewerte nach deren Prüfung und Ausnahmebehandler, die Fehler stillschweigend unterdrücken. Wenn Fehler erkannt, aber nicht behandelt werden, setzt das Programm die Ausführung in einem unerwarteten oder ungültigen Zustand fort, was potenziell zu Sicherheitsschwachstellen, Abstürzen oder Datenbeschädigung führt.

Risiko

Das Ignorieren erkannter Fehler lässt Systeme in unerwarteten Zuständen, die Angreifer ausnutzen können. Speicherzuweisungsfehler, die erkannt aber ignoriert werden, führen zu Null-Pointer-Dereferenzierungen oder Verwendung nicht initialisierten Speichers. Dateioperationsfehler, die stillschweigend unterdrückt werden, können zu unvollständiger Datenverarbeitung oder Ressourcenlecks führen. Sicherheitsprüfungen, die Fehler erkennen aber trotzdem fortfahren, negieren den Zweck der Prüfung. Angreifer können absichtlich Fehlerbedingungen auslösen, um die Anwendung in verwundbare Zustände zu zwingen. Die stillschweigende Natur dieser Fehler macht die Fehlersuche schwierig und kann aktive Ausnutzung verbergen.

Lösung

Behandeln Sie jede Ausnahme ordnungsgemäß, indem Sie sicherstellen, dass alle erkannten Fehler so adressiert werden, dass der Systemzustand vorhersagbar bleibt. Wenn Funktionen Fehler zurückgeben, beheben Sie das Problem und versuchen Sie es erneut, informieren Sie Benutzer und fahren Sie ordnungsgemäß fort, oder informieren Sie und beenden Sie ordnungsgemäß mit korrekter Bereinigung. Verwenden Sie niemals leere Catch-Blöcke -- protokollieren Sie mindestens den Fehler für Debugging-Zwecke. Nutzen Sie umfangreiche Testverfahren einschließlich Ad-hoc-Tests, Äquivalenzpartitionierung, Robustheitstests, Mutationstests und Fuzzing, um unbehandelte Fehler zu entdecken. Etablieren Sie Kodierungsstandards, die leere Ausnahmebehandler verbieten.

Häufige Auswirkungen

AuswirkungDetails
IntegritätBereich: Integrität

Ein Angreifer könnte ignorierte Fehlerbedingungen ausnutzen, um Systeme in unerwartete Zustände zu versetzen.
AndereBereich: Ändere

Veränderte Ausführungslogik -- Unbeabsichtigte Codepfade können ausgeführt werden und schädliches Verhalten ermöglichen.

Beispielcode und Lösung

Verwundbarer Code

// Verwundbar: Fehler erkannt aber keine Aktion unternommen
void vulnerable_read_file(const char *filename) {
    FILE *f = fopen(filename, "r");
    char buffer[1024];

    // Verwundbar: Fehler erkannt aber ignoriert
    if (f == NULL) {
        // Leerer Block - Fehler erkannt aber keine Aktion!
    }

    // Fährt mit NULL-Dateizeiger fort - Absturz oder undefiniertes Verhalten
    fread(buffer, 1, sizeof(buffer), f);
    fclose(f);
}

// Verwundbar: Speicherzuweisungsfehler ignoriert
void vulnerable_process_data(size_t size) {
    char *buffer = malloc(size);

    if (buffer == NULL) {
        // Verwundbar: Erkannt aber keine Aktion
        perror("malloc failed");
        // Fällt durch und verwendet NULL-Pointer!
    }

    memset(buffer, 0, size);  // Absturz!
}
// Verwundbar: Leerer Catch-Block
public class VulnerableFileReader {

    public String readFile(String path) {
        FileInputStream fis = null;
        String content = null;

        try {
            fis = new FileInputStream(path);
            byte[] data = new byte[fis.available()];
            fis.read(data);
            content = new String(data);
        } catch (IOException e) {
            // Verwundbar: Ausnahme gefangen aber komplett ignoriert!
            // Kein Logging, keine Benachrichtigung, keine Behandlung
        }

        return content;  // Kann unerwartet null zurückgeben
    }

    public void processConfig(String configPath) {
        try {
            Config config = loadConfig(configPath);
            applyConfig(config);
        } catch (ConfigException e) {
            // Verwundbar: Konfigurations fehler stillschweigend ignorieren
            // System fährt mit Standard-/alter Konfiguration fort
        }
    }
}

// Verwundbar: Rückgabewert geprüft aber nicht behandelt
public class VulnerableAuth {
    public void authenticate(String username, String password) {
        boolean valid = validateCredentials(username, password);

        if (!valid) {
            // Verwundbar: Ungültige Zugangsdaten erkannt aber fortgefahren!
            logger.warn("Invalid credentials");
            // Fällt durch und gewährt Zugang
        }

        grantAccess(username);
    }
}
# Verwundbar: Ausnahme gefangen aber ignoriert
def vulnerable_connect_db():
    connection = None

    try:
        connection = database.connect()
    except DatabaseError as e:
        pass  # Verwundbar: Fehler stillschweigend ignoriert!

    # Fährt mit None-Verbindung fort
    return connection.execute("SELECT * FROM users")  # Absturz!

# Verwundbar: Fehler protokolliert aber Ausführung fährt unsicher fort
def vulnerable_write_file(filename, data):
    try:
        with open(filename, 'w') as f:
            f.write(data)
    except IOError as e:
        print(f"Write failed: {e}")
        # Verwundbar: Fährt fort als ob Schreibvorgang erfolgreich war

    # Meldet Erfolg auch bei Fehler
    notify_success("File written: " + filename)

Sichere Lösung

// Behoben: Ordnungsgemäße Fehlerbehandlung mit Rückgabecodes
int secure_read_file(const char *filename, char *buffer, size_t bufsize) {
    FILE *f = fopen(filename, "r");

    // Behoben: Fehlercode bei Misserfolg zurückgeben
    if (f == NULL) {
        fprintf(stderr, "Failed to open file: %s\n", filename);
        return -1;
    }

    size_t bytes_read = fread(buffer, 1, bufsize - 1, f);

    if (ferror(f)) {
        fprintf(stderr, "Error reading file: %s\n", filename);
        fclose(f);
        return -1;
    }

    buffer[bytes_read] = '\0';
    fclose(f);
    return 0;
}

// Behoben: Zuweisungsfehler ordnungsgemäß behandeln
int secure_process_data(size_t size) {
    char *buffer = malloc(size);

    // Behoben: Fehler zurückgeben oder abbrechen bei Zuweisungsfehler
    if (buffer == NULL) {
        perror("malloc failed");
        return -1;  // Oder abort() für kritische Fehler
    }

    memset(buffer, 0, size);
    process_buffer(buffer, size);
    free(buffer);
    return 0;
}
// Behoben: Ordnungsgemäße Ausnahmebehandlung
public class SecureFileReader {

    public String readFile(String path) throws IOException {
        // Behoben: Ausnahme propagieren lassen, wenn Aufrufer nicht behandeln kann
        try (FileInputStream fis = new FileInputStream(path)) {
            byte[] data = new byte[fis.available()];
            fis.read(data);
            return new String(data);
        }
    }

    public String readFileWithFallback(String path) {
        try (FileInputStream fis = new FileInputStream(path)) {
            byte[] data = new byte[fis.available()];
            fis.read(data);
            return new String(data);
        } catch (IOException e) {
            // Behoben: Fehler protokollieren und Fallback bereitstellen
            logger.error("Failed to read file: " + path, e);
            return getDefaultContent();  // Oder Laufzeitausnahme werfen
        }
    }

    public void processConfig(String configPath) throws ConfigException {
        try {
            Config config = loadConfig(configPath);
            applyConfig(config);
        } catch (ConfigException e) {
            // Behoben: Protokollieren, alarmieren und erneut werfen
            logger.error("Config load failed", e);
            alertAdmin("Configuration error: " + e.getMessage());
            throw e;  // Aufrufer entscheiden lassen, wie fortgefahren wird
        }
    }
}

// Behoben: Auf erkannten Authentifizierungsfehler reagieren
public class SecureAuth {
    public void authenticate(String username, String password)
            throws AuthenticationException {
        boolean valid = validateCredentials(username, password);

        if (!valid) {
            // Behoben: Bei ungültigen Zugangsdaten Maßnahmen ergreifen
            logger.warn("Invalid credentials for user: " + username);
            auditLog.logFailedAttempt(username);
            throw new AuthenticationException("Invalid credentials");
        }

        grantAccess(username);
    }
}
# Behoben: Ordnungsgemäße Ausnahmebehandlung
def secure_connect_db():
    try:
        connection = database.connect()
        return connection
    except DatabaseError as e:
        # Behoben: Protokollieren und erneut werfen oder Sentinel-Wert zurückgeben
        logger.error(f"Database connection failed: {e}")
        raise  # Oder None mit klarer Dokumentation zurückgeben

# Behoben: Datei-Schreibfehler ordnungsgemäß behandeln
def secure_write_file(filename, data):
    try:
        with open(filename, 'w') as f:
            f.write(data)
    except IOError as e:
        logger.error(f"Write failed: {e}")
        # Behoben: Keinen Erfolg melden, Fehler propagieren
        raise WriteError(f"Failed to write {filename}") from e

    # Nur Erfolg melden, wenn Schreibvorgang tatsächlich erfolgreich war
    notify_success(f"File written: {filename}")

# Behoben: Status an Aufrufer zurückgeben
def secure_process_data(data):
    try:
        result = process(data)
        return {"success": True, "result": result}
    except ProcessingError as e:
        logger.error(f"Processing failed: {e}")
        # Behoben: Fehlerstatus zurückgeben anstatt stillschweigend fortzufahren
        return {"success": False, "error": str(e)}

CVE-Beispiele

  • CVE-2022-21820 -- GPU-Rechenzentrumsmanager ignoriert fehlerhafte Anfragefehler und verursacht Speicherbeschädigung.

Referenzen

  1. MITRE Corporation. "CWE-390: Detection of Error Condition Without Action." https://cwe.mitre.org/data/definitions/390.html
  2. CERT. "ERR00-C. Adopt and implement a consistent and comprehensive error-handling policy."