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
| Auswirkung | Details |
|---|---|
| Integrität | Umfang: Integrität Ausführungslogik ändern - Unbehandelte Fälle können dazu führen, dass das Programm sicherheitskritische Operationen überspringt oder unerwartete Codepfade nimmt. |
| Andere | Umfang: 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
- MITRE Corporation. "CWE-478: Missing Default Case in Multiple Condition Expression." https://cwe.mitre.org/data/definitions/478.html
- CERT C Secure Coding Standard. "MSC01-C. Strive for logical completeness."
- SEI CERT Oracle Coding Standard for Java. "MSC57-J. Strive for logical completeness."