Fehlender Default-Fall in Mehrfachbedingungsausdruck

Beschreibung

Fehlender Default-Fall in Mehrfachbedingungsausdruck ist eine Schwachstelle, bei der Code keinen Default-Fall in einem Ausdruck mit mehreren Bedingungen hat, wie z.B. einer Switch-Anweisung. Wenn Mehrfachbedingungsausdrücke wie Switch-Anweisungen keinen Default-Fall haben und nicht alle möglichen Eingabewerte berücksichtigen, erzeugen sie logische Fehler, die zu Sicherheitsproblemen führen können. Unerwartete Werte können dazu führen, dass der Code sicherheitskritische Operationen überspringt, nicht initialisierte Variablen verwendet oder anderes undefiniertes Verhalten zeigt.

Risiko

Fehlende Default-Fälle erzeugen unvorhersehbares Verhalten, wenn unerwartete Werte auftreten. Sicherheitsprüfungen können umgangen werden, wenn eine Switch-Anweisung, die Autorisierung behandelt, zurückkehrt ohne ein Ergebnis für unbehandelte Fälle zu setzen. Nicht initialisierte Variablen, die nach einem Switch verwendet werden, können sensible Daten aus vorherigen Operationen enthalten. In Zustandsautomaten können fehlende Default-Fälle das System in undefinierten Zuständen belassen. Angreifer können Eingaben speziell gestalten, um unbehandelte Fälle auszulösen und das resultierende Verhalten auszunutzen.

Lösung

Implementieren Sie einen Default-Fall in allen Switch-artigen Anweisungen, um unbehandelte Bedingungen zu berücksichtigen. Der Default-Fall sollte gegen ungültige Eingaben validieren anstatt eine Standardoption anzunehmen - den Default als Fehlerbehandlung zu behandeln ist kritische Praxis. Protokollieren Sie unerwartete Werte in Default-Fällen für Debugging und Sicherheitsüberwachung. Überlegen Sie, ob der Default eine Exception werfen, einen Fehlercode zurückgeben oder den Fall ordnungsgemäß behandeln sollte. Stellen Sie sicher, dass alle Enum-Werte behandelt werden, wenn über Enum-Typen geswitcht wird, und schließen Sie trotzdem einen Default für Zukunftssicherheit ein.

Häufige Auswirkungen

AuswirkungDetails
IntegritätUmfang: Integrität

Ausführungslogik ändern - Unbehandelte Fälle können dazu führen, dass das Programm sicherheitskritische Operationen überspringt oder unerwartete Codepfade nimmt.
AndereUmfang: Vertraulichkeit, Autorisierung, Verfügbarkeit

Variiert nach Kontext - Abhängig von den Umständen können fehlende Default-Fälle zu Authentifizierungsumgehung, Autorisierungsfehlern oder anderen Sicherheitsproblemen führen.

Beispielcode

Anfälliger Code

// Anfällig: Sicherheitsprüfung mit fehlendem Default
#include <stdio.h>

int vulnerable_permission_check(int user_role) {
    int permission = -1;  // Absicht nicht initialisiert, aber siehe unten

    switch (user_role) {
        case 0:  // Gast
            permission = 0;
            break;
        case 1:  // Benutzer
            permission = 1;
            break;
        case 2:  // Admin
            permission = 2;
            break;
        // Anfällig: Kein Default-Fall
        // Wenn user_role 99 ist, bleibt permission -1
    }

    // Anfällig: -1 könnte als Fehler oder hohe Berechtigung interpretiert werden
    // abhängig davon wie der aufrufende Code damit umgeht
    return permission;
}

// Anfällig: Zustandsautomat mit fehlenden Übergängen
typedef enum { STATE_INIT, STATE_AUTH, STATE_ACTIVE, STATE_CLOSED } State;

void vulnerable_state_machine(State current, int event) {
    switch (current) {
        case STATE_INIT:
            if (event == 1) transition_to(STATE_AUTH);
            break;
        case STATE_AUTH:
            if (event == 2) transition_to(STATE_ACTIVE);
            break;
        case STATE_ACTIVE:
            if (event == 3) transition_to(STATE_CLOSED);
            break;
        // Anfällig: Kein Fall für STATE_CLOSED
        // Anfällig: Kein Default für ungültige Zustände
    }
    // Unbekannter Zustand oder Event belässt System in undefiniertem Zustand
}
// Anfällig: Zinssatzberechnung ohne Default
public class VulnerableRateCalculator {

    public double getInterestRate(int creditScore) {
        double rate;

        switch (creditScore / 100) {  // 700+ = 7, 600-699 = 6, usw.
            case 8:
            case 7:
                rate = 0.05;  // Gute Bonität
                break;
            case 6:
                rate = 0.08;  // Mittlere Bonität
                break;
            case 5:
                rate = 0.12;  // Schlechte Bonität
                break;
            // Anfällig: Kein Default-Fall
            // Score 400 (case 4) oder 900+ (case 9) nicht behandelt
        }

        // Anfällig: rate kann nicht initialisiert sein (Java-Compiler fängt dies möglicherweise ab)
        return rate;
    }

    // Anfällig: Enum-Switch ohne vollständige Behandlung
    public String getAccessLevel(UserRole role) {
        switch (role) {
            case GUEST:
                return "read";
            case USER:
                return "read,write";
            case ADMIN:
                return "read,write,delete";
            // Anfällig: Wenn Enum mit neuem Wert erweitert wird, keine Behandlung
            // Anfällig: Kein Default-Fall
        }
        return null;  // Anfällig: null kann NPE verursachen
    }
}
# Anfällig: Match-Case ohne Default (Python 3.10+)
def vulnerable_process_result(result_code):
    match result_code:
        case 0:
            return "Erfolg"
        case 1:
            return "Teilerfolg"
        case 2:
            return "Fehlschlag"
        # Anfällig: Kein case _ (Wildcard/Default)

    # Anfällig: Fällt durch ohne Rückgabe für unbekannte Codes
    # Gibt implizit None zurück

# Anfällig: Dictionary-basierter Switch ohne Default
def vulnerable_command_handler(command):
    handlers = {
        'create': handle_create,
        'read': handle_read,
        'update': handle_update,
        'delete': handle_delete
    }

    # Anfällig: KeyError wenn command nicht in handlers
    handler = handlers[command]
    return handler()
// Anfällig: Switch ohne Default
function vulnerableCalculate(operation, a, b) {
    let result;

    switch (operation) {
        case 'add':
            result = a + b;
            break;
        case 'subtract':
            result = a - b;
            break;
        case 'multiply':
            result = a * b;
            break;
        case 'divide':
            result = a / b;
            break;
        // Anfällig: Kein Default-Fall
        // 'modulo' oder Tippfehler 'ad' lässt result undefiniert
    }

    // Anfällig: result kann undefiniert sein
    return result;
}

// Anfällig: Autorisierungsprüfung
function vulnerableCheckPermission(action) {
    switch (action) {
        case 'view':
            return true;  // Jeder kann ansehen
        case 'edit':
            return isEditor();
        case 'delete':
            return isAdmin();
        // Anfällig: Kein Default
        // Neue Aktion 'export' wäre implizit erlaubt (undefined -> truthy in manchen Kontexten)
    }
}

Korrigierter Code

// Korrigiert: Sicherheitsprüfung mit ordnungsgemäßer Default-Behandlung
#include <stdio.h>
#include <stdlib.h>

int secure_permission_check(int user_role) {
    switch (user_role) {
        case 0:  // Gast
            return 0;
        case 1:  // Benutzer
            return 1;
        case 2:  // Admin
            return 2;
        default:
            // Korrigiert: Explizite Behandlung unerwarteter Werte
            log_security_event("Unbekannte Benutzerrolle: %d", user_role);
            return -1;  // Expliziter Fehlerwert
    }
}

// Alternative: Fail-Safe mit explizitem Fehler
typedef struct {
    int permission;
    int error;
} PermissionResult;

PermissionResult secure_permission_check_v2(int user_role) {
    PermissionResult result = {0, 0};

    switch (user_role) {
        case 0:
            result.permission = 0;
            break;
        case 1:
            result.permission = 1;
            break;
        case 2:
            result.permission = 2;
            break;
        default:
            // Korrigiert: Fehler-Flag für unbekannte Rolle setzen
            result.error = 1;
            result.permission = 0;  // Standard auf niedrigste Berechtigung
            break;
    }

    return result;
}

// Korrigiert: Zustandsautomat mit vollständiger Behandlung
void secure_state_machine(State current, int event) {
    switch (current) {
        case STATE_INIT:
            if (event == 1) transition_to(STATE_AUTH);
            else log_unexpected_event(current, event);
            break;
        case STATE_AUTH:
            if (event == 2) transition_to(STATE_ACTIVE);
            else if (event == -1) transition_to(STATE_INIT);  // Timeout
            else log_unexpected_event(current, event);
            break;
        case STATE_ACTIVE:
            if (event == 3) transition_to(STATE_CLOSED);
            else log_unexpected_event(current, event);
            break;
        case STATE_CLOSED:
            // Korrigiert: Closed-Zustand behandeln
            log_info("Event in geschlossenem Zustand ignoriert");
            break;
        default:
            // Korrigiert: Ungültige Zustände behandeln
            log_error("Ungültiger Zustand: %d", current);
            transition_to(STATE_INIT);  // Auf bekannten Zustand zurücksetzen
            break;
    }
}
// Korrigiert: Vollständige Switch-Behandlung
public class SecureRateCalculator {

    public double getInterestRate(int creditScore) {
        int scoreRange = creditScore / 100;

        switch (scoreRange) {
            case 8:
            case 7:
                return 0.05;
            case 6:
                return 0.08;
            case 5:
                return 0.12;
            case 4:
            case 3:
                return 0.18;  // Sehr schlechte Bonität
            default:
                // Korrigiert: Grenzfälle behandeln
                if (scoreRange >= 9) {
                    return 0.03;  // Ausgezeichnete Bonität
                } else {
                    // Score zu niedrig oder ungültig
                    throw new IllegalArgumentException(
                        "Ungültiger Credit Score: " + creditScore
                    );
                }
        }
    }

    // Korrigiert: Vollständige Enum-Behandlung mit Default
    public String getAccessLevel(UserRole role) {
        switch (role) {
            case GUEST:
                return "read";
            case USER:
                return "read,write";
            case ADMIN:
                return "read,write,delete";
            default:
                // Korrigiert: Neue Enum-Werte ordnungsgemäß behandeln
                logger.warn("Unbekannte Rolle: " + role);
                return "read";  // Fail-Safe: Minimale Berechtigungen
        }
    }
}
# Korrigiert: Match-Case mit Default
def secure_process_result(result_code):
    match result_code:
        case 0:
            return "Erfolg"
        case 1:
            return "Teilerfolg"
        case 2:
            return "Fehlschlag"
        case _:  # Korrigiert: Wildcard-Default-Fall
            logger.warning(f"Unbekannter Ergebniscode: {result_code}")
            return f"Unbekannt ({result_code})"

# Korrigiert: Dictionary-basierter Switch mit Default
def secure_command_handler(command):
    handlers = {
        'create': handle_create,
        'read': handle_read,
        'update': handle_update,
        'delete': handle_delete
    }

    # Korrigiert: .get() mit Default verwenden
    handler = handlers.get(command)

    if handler is None:
        # Korrigiert: Unbekanntes Kommando explizit behandeln
        logger.warning(f"Unbekanntes Kommando: {command}")
        raise ValueError(f"Unbekanntes Kommando: {command}")

    return handler()
// Korrigiert: Switch mit ordnungsgemäßem Default
function secureCalculate(operation, a, b) {
    switch (operation) {
        case 'add':
            return a + b;
        case 'subtract':
            return a - b;
        case 'multiply':
            return a * b;
        case 'divide':
            if (b === 0) {
                throw new Error('Division durch Null');
            }
            return a / b;
        default:
            // Korrigiert: Expliziter Fehler für unbekannte Operation
            throw new Error(`Unbekannte Operation: ${operation}`);
    }
}

// Korrigiert: Autorisierung mit sicherem Default
function secureCheckPermission(action) {
    switch (action) {
        case 'view':
            return true;
        case 'edit':
            return isEditor();
        case 'delete':
            return isAdmin();
        default:
            // Korrigiert: Unbekannte Aktionen werden standardmäßig verweigert
            console.warn(`Unbekannte Aktion verweigert: ${action}`);
            return false;  // Fail-Safe: Unbekannte Aktionen verweigern
    }
}

CVE-Beispiele

Keine spezifischen CVEs sind in der MITRE-Datenbank für dieses CWE aufgelistet. Das Muster ist jedoch dokumentiert in:

  • CLASP-Taxonomie
  • Software Fault Patterns (SFP4)
  • Sicherheitsprobleme in Zustandsautomaten und Autorisierungslogik

Referenzen

  1. MITRE Corporation. "CWE-478: Missing Default Case in Multiple Condition Expression." https://cwe.mitre.org/data/definitions/478.html
  2. CERT C Secure Coding Standard. "MSC01-C. Strive for logical completeness."
  3. SEI CERT Oracle Coding Standard for Java. "MSC57-J. Strive for logical completeness."