Fehlendes standardisiertes Fehlerbehandlungsmechanismus

Beschreibung

Fehlendes standardisiertes Fehlerbehandlungsmechanismus ist eine Schwachstelle, bei der ein Produkt keine standardisierte Methode zur Fehlerbehandlung im gesamten Code verwendet, was zu inkonsistenter Fehlerbehandlung führt. Ohne einen zentralisierten oder standardisierten Ansatz können verschiedene Teile der Anwendung Fehler unterschiedlich behandeln—manche protokollieren sie, manche ignorieren sie stillschweigend, manche legen Details an Benutzer offen. Diese Inkonsistenz erzeugt Lücken, in denen Fehler falsch behandelt werden können, sicherheitsrelevante Fehler unentdeckt bleiben können und das Verhalten der Anwendung während Fehlerbedingungen unvorhersehbar wird.

Risiko

Inkonsistente Fehlerbehandlung erzeugt mehrere Sicherheitsrisiken. Wenn einige Codepfade Fehler ordnungsgemäß behandeln, während andere es nicht tun, können Angreifer nach Schwachstellen suchen, indem sie Fehler in verschiedenen Bereichen auslösen. Fehlerbedingungen, die die Protokollierung umgehen, ermöglichen unentdeckte Angriffe. Inkonsistente benutzerorientierte Fehlermeldungen können in einigen Fällen sensible Informationen offenlegen, in anderen nicht, was Aufklärungsmöglichkeiten schafft. Ohne standardisierte Behandlung vergessen Entwickler möglicherweise, bestimmte Fehlertypen zu behandeln, was zu nicht abgefangenen Ausnahmen führt, die die Anwendung zum Absturz bringen oder in einem unsicheren Zustand belassen. Der Mangel an Einheitlichkeit macht Sicherheitsaudits auch schwierig, da Prüfer jede Fehlerbehandlungsinstanz einzeln untersuchen müssen.

Lösung

Implementieren Sie einen standardisierten Fehlerbehandlungsmechanismus in der gesamten Anwendung. Erstellen Sie zentralisierte Fehlerbehandlungskomponenten oder Middleware, die alle Fehler einheitlich verarbeiten. Definieren Sie Fehlerbehandlungsrichtlinien, die spezifizieren, wie verschiedene Fehlerkategorien protokolliert, gemeldet und Benutzern präsentiert werden sollen. Verwenden Sie Ausnahmehierarchien und Fehlercodes, um Fehler konsistent zu kategorisieren. Implementieren Sie globale Ausnahmehandler als Sicherheitsnetz. Stellen Sie sicher, dass alle Fehler mit angemessenen Details für Debugging protokolliert werden, während Benutzern bereinigte Meldungen präsentiert werden. Erstellen Sie Fehlerbehandlungsdokumentation und erzwingen Sie Standards durch Code-Review und statische Analysetools.

Häufige Auswirkungen

AuswirkungDetails
IntegritätBereich: Integrität

Ausführungslogik ändern - Inkonsistente Fehlerbehandlung kann bewirken, dass die Anwendung sich unvorhersehbar verhält, potenziell Sicherheitsprüfungen umgeht oder in unsichere Zustände eintritt.
VerantwortlichkeitBereich: Verantwortlichkeit, Nichtabstreitbarkeit

Aktivitäten verbergen - Ohne standardisierte Fehlerprotokollierung können sicherheitsrelevante Ereignisse unaufgezeichnet bleiben, was die Erkennung von Angriffen verhindert und forensische Analyse behindert.
VertraulichkeitBereich: Vertraulichkeit

Anwendungsdaten lesen - Inkonsistente Fehlermeldungen können in einigen Codepfaden sensible Informationen offenlegen, während sie in anderen ordnungsgemäß verborgen werden.

Beispielcode + Lösungscode

Verwundbarer Code

// Verwundbar: Keine standardisierte Fehlerbehandlung
public class VerwundbareAnwendung {

    public void processPayment(String userId, double amount) {
        // Verwundbar: Verschiedene Fehlerbehandlungsansätze überall verstreut
        try {
            User user = userService.findUser(userId);
            if (user == null) {
                // Einfach stillschweigend zurückkehren - keine Protokollierung
                return;
            }
        } catch (DatabaseException e) {
            // Stack-Trace auf Konsole ausgeben - könnte sichtbar sein
            e.printStackTrace();
        }

        try {
            paymentService.charge(userId, amount);
        } catch (PaymentException e) {
            // Rohe Ausnahme werfen - legt Details offen
            throw new RuntimeException("Zahlung fehlgeschlagen: " + e.getMessage());
        }
    }

    public void updateProfile(String userId, Map<String, String> data) {
        try {
            validateInput(data);
        } catch (ValidationException e) {
            // Anderer Ansatz: protokollieren und schlucken
            System.out.println("Validierung fehlgeschlagen: " + e);
        }

        try {
            profileService.update(userId, data);
        } catch (Exception e) {
            // Noch ein anderer Ansatz: generisches Catch-All
            // Sicherheitsfehler mit regulären Fehlern gemischt
        }
    }

    public String fetchDocument(String docId) {
        try {
            return documentService.get(docId);
        } catch (AccessDeniedException e) {
            // Verwundbar: Legt Autorisierungsdetails offen
            return "Fehler: Sie haben keine Berechtigung auf " + docId +
                   " zuzugreifen (erforderliche Rolle: " + e.getRequiredRole() + ")";
        } catch (DocumentNotFoundException e) {
            // Anderes Nachrichtenformat
            return null;  // Aufrufer weiß nicht, ob nicht gefunden vs. Fehler
        }
    }
}
# Verwundbar: Inkonsistente Fehlerbehandlung über Module
# module_a.py
def process_order(order_id):
    try:
        order = db.get_order(order_id)
    except DatabaseError as e:
        # Verwundbar: Protokolliert sensible Abfrage
        print(f"Datenbankfehler: {e.query}")
        return None

# module_b.py
def update_inventory(product_id, quantity):
    try:
        inventory.update(product_id, quantity)
    except Exception:
        # Verwundbar: Schluckt stillschweigend alle Fehler
        pass

# module_c.py
def charge_customer(customer_id, amount):
    try:
        payment.process(customer_id, amount)
    except PaymentError as e:
        # Verwundbar: Legt Details an Benutzer offen
        raise ValueError(f"Zahlung fehlgeschlagen für {customer_id}: {e.details}")
    except Exception as e:
        # Verwundbar: Re-raises mit vollständigem Stack-Trace
        raise

# module_d.py
def send_notification(user_id, message):
    # Verwundbar: Überhaupt keine Fehlerbehandlung
    notification_service.send(user_id, message)
// Verwundbar: Inkonsistente Express.js-Fehlerbehandlung
const express = require('express');
const app = express();

// Route 1: Gibt Fehlerdetails an Client zurück
app.get('/api/users/:id', async (req, res) => {
    try {
        const user = await db.getUser(req.params.id);
        res.json(user);
    } catch (error) {
        // Verwundbar: Vollständiger Fehler offengelegt
        res.status(500).json({ error: error.message, stack: error.stack });
    }
});

// Route 2: Stiller Fehler
app.post('/api/orders', async (req, res) => {
    try {
        await orderService.create(req.body);
        res.json({ success: true });
    } catch (error) {
        // Verwundbar: Keine Protokollierung, keine nützliche Antwort
        res.json({ success: false });
    }
});

// Route 3: Anderes Fehlerformat
app.put('/api/settings', async (req, res) => {
    try {
        await settingsService.update(req.body);
        res.sendStatus(200);
    } catch (error) {
        // Anderes Format als andere Routen
        res.status(400).send(error.toString());
    }
});

// Kein globaler Fehlerhandler - unbehandelte Fehler crashen die App
<?php
// Verwundbar: Keine standardisierte Fehlerbehandlung
class VerwundbarerController {

    public function createUser($data) {
        // Verwundbar: Fehlerunterdrückung
        $result = @$this->userService->create($data);
        if (!$result) {
            // Keine Anzeige, was schiefging
            return false;
        }
        return $result;
    }

    public function deleteUser($id) {
        try {
            $this->userService->delete($id);
        } catch (PDOException $e) {
            // Verwundbar: Datenbankfehlerdetails offengelegt
            die("Datenbankfehler: " . $e->getMessage());
        }
    }

    public function updateUser($id, $data) {
        try {
            $this->userService->update($id, $data);
            return true;
        } catch (Exception $e) {
            // Verwundbar: Protokolliert in öffentlich zugängliche Datei
            error_log($e->getMessage(), 3, '/var/www/html/errors.log');
            return false;
        }
    }

    public function getUser($id) {
        // Verwundbar: Überhaupt keine Fehlerbehandlung
        return $this->userService->find($id);
    }
}
?>

Lösungscode

// Behoben: Standardisiertes Fehlerbehandlungsmechanismus
public class SichereAnwendung {
    private static final Logger logger = LoggerFactory.getLogger(SichereAnwendung.class);
    private final ErrorHandler errorHandler;

    public SichereAnwendung(ErrorHandler errorHandler) {
        this.errorHandler = errorHandler;
    }

    public Result<Void> processPayment(String userId, double amount) {
        try {
            User user = userService.findUser(userId);
            if (user == null) {
                return Result.failure(ErrorCode.USER_NOT_FOUND, "Benutzer nicht gefunden");
            }

            paymentService.charge(userId, amount);
            return Result.success();

        } catch (DatabaseException e) {
            return errorHandler.handle(e, ErrorCategory.DATABASE);
        } catch (PaymentException e) {
            return errorHandler.handle(e, ErrorCategory.PAYMENT);
        } catch (Exception e) {
            return errorHandler.handleUnexpected(e);
        }
    }

    public Result<Void> updateProfile(String userId, Map<String, String> data) {
        try {
            validateInput(data);
            profileService.update(userId, data);
            return Result.success();

        } catch (ValidationException e) {
            return errorHandler.handle(e, ErrorCategory.VALIDATION);
        } catch (Exception e) {
            return errorHandler.handleUnexpected(e);
        }
    }
}

// Behoben: Zentralisierter Fehlerhandler
public class ErrorHandler {
    private static final Logger logger = LoggerFactory.getLogger(ErrorHandler.class);

    public <T> Result<T> handle(Exception e, ErrorCategory category) {
        // Behoben: Konsistente Protokollierung für alle Fehler
        String errorId = generateErrorId();
        logger.error("Fehler [{}] in {}: {}", errorId, category, e.getMessage(), e);

        // Behoben: Konsistente benutzerorientierte Meldungen
        String userMessage = getUserMessage(category);
        return Result.failure(errorId, userMessage);
    }

    public <T> Result<T> handleUnexpected(Exception e) {
        String errorId = generateErrorId();
        logger.error("Unerwarteter Fehler [{}]: {}", errorId, e.getMessage(), e);

        // Behoben: Niemals unerwartete Fehlerdetails offenlegen
        return Result.failure(errorId, "Ein unerwarteter Fehler ist aufgetreten. Referenz: " + errorId);
    }

    private String getUserMessage(ErrorCategory category) {
        // Behoben: Standardisierte, nicht-offenlegende Meldungen
        switch (category) {
            case DATABASE: return "Operation könnte nicht abgeschlossen werden. Bitte versuchen Sie es erneut.";
            case PAYMENT: return "Zahlung könnte nicht verarbeitet werden. Bitte überprüfen Sie Ihre Zahlungsdetails.";
            case VALIDATION: return "Ungültige Eingabe. Bitte überprüfen Sie Ihre Daten.";
            case AUTHORIZATION: return "Sie haben keine Berechtigung für diese Aktion.";
            default: return "Ein Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.";
        }
    }

    private String generateErrorId() {
        return UUID.randomUUID().toString().substring(0, 8);
    }
}

// Behoben: Result-Typ für konsistente Rückgabewerte
public class Result<T> {
    private final boolean success;
    private final T data;
    private final String errorId;
    private final String errorMessage;

    // Konstruktoren, Getter, Factory-Methoden...
}
# Behoben: Standardisiertes Fehlerbehandlungs-Framework
import logging
import uuid
from functools import wraps
from enum import Enum

logger = logging.getLogger(__name__)

class ErrorCategory(Enum):
    DATABASE = "database"
    VALIDATION = "validation"
    AUTHORIZATION = "authorization"
    EXTERNAL_SERVICE = "external_service"
    UNEXPECTED = "unexpected"

class AppError(Exception):
    """Basis-Anwendungsfehler mit standardisierter Struktur."""
    def __init__(self, category: ErrorCategory, message: str, details: dict = None):
        self.error_id = str(uuid.uuid4())[:8]
        self.category = category
        self.message = message
        self.details = details or {}
        super().__init__(message)

class ErrorHandler:
    """Zentralisierter Fehlerhandler für konsistente Verarbeitung."""

    USER_MESSAGES = {
        ErrorCategory.DATABASE: "Operation könnte nicht abgeschlossen werden. Bitte versuchen Sie es erneut.",
        ErrorCategory.VALIDATION: "Ungültige Eingabe. Bitte überprüfen Sie Ihre Daten.",
        ErrorCategory.AUTHORIZATION: "Sie haben keine Berechtigung für diese Aktion.",
        ErrorCategory.EXTERNAL_SERVICE: "Service vorübergehend nicht verfügbar.",
        ErrorCategory.UNEXPECTED: "Ein unerwarteter Fehler ist aufgetreten.",
    }

    @classmethod
    def handle(cls, error: Exception, category: ErrorCategory = None) -> dict:
        """Fehler mit konsistenter Protokollierung und Antwort behandeln."""
        error_id = getattr(error, 'error_id', str(uuid.uuid4())[:8])

        if category is None:
            category = getattr(error, 'category', ErrorCategory.UNEXPECTED)

        # Behoben: Konsistente Protokollierung mit Fehler-ID
        logger.error(
            f"Fehler [{error_id}] {category.value}: {str(error)}",
            exc_info=True,
            extra={'error_id': error_id, 'category': category.value}
        )

        # Behoben: Konsistente benutzerorientierte Antwort
        return {
            'success': False,
            'error_id': error_id,
            'message': cls.USER_MESSAGES.get(category, cls.USER_MESSAGES[ErrorCategory.UNEXPECTED])
        }

def handle_errors(category: ErrorCategory = ErrorCategory.UNEXPECTED):
    """Decorator für standardisierte Fehlerbehandlung."""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            try:
                result = func(*args, **kwargs)
                return {'success': True, 'data': result}
            except AppError as e:
                return ErrorHandler.handle(e, e.category)
            except Exception as e:
                return ErrorHandler.handle(e, category)
        return wrapper
    return decorator

# Behoben: Verwendung mit konsistenter Fehlerbehandlung
@handle_errors(ErrorCategory.DATABASE)
def process_order(order_id):
    order = db.get_order(order_id)
    return order

@handle_errors(ErrorCategory.DATABASE)
def update_inventory(product_id, quantity):
    inventory.update(product_id, quantity)
    return True

@handle_errors(ErrorCategory.EXTERNAL_SERVICE)
def charge_customer(customer_id, amount):
    payment.process(customer_id, amount)
    return True
// Behoben: Standardisierte Express.js-Fehlerbehandlung
const express = require('express');
const winston = require('winston');
const { v4: uuidv4 } = require('uuid');

const app = express();

// Behoben: Zentralisierter Logger
const logger = winston.createLogger({
    level: 'error',
    format: winston.format.json(),
    transports: [new winston.transports.File({ filename: 'error.log' })]
});

// Behoben: Anwendungsfehlerklasse
class AppError extends Error {
    constructor(category, message, statusCode = 500) {
        super(message);
        this.errorId = uuidv4().substring(0, 8);
        this.category = category;
        this.statusCode = statusCode;
        this.isOperational = true;
    }
}

// Behoben: Fehlerkategorien
const ErrorCategory = {
    VALIDATION: 'validation',
    DATABASE: 'database',
    AUTHORIZATION: 'authorization',
    NOT_FOUND: 'not_found'
};

// Behoben: Standardisierte Benutzermeldungen
const userMessages = {
    [ErrorCategory.VALIDATION]: 'Ungültige Eingabe.',
    [ErrorCategory.DATABASE]: 'Operation könnte nicht abgeschlossen werden.',
    [ErrorCategory.AUTHORIZATION]: 'Sie haben keine Berechtigung für diese Aktion.',
    [ErrorCategory.NOT_FOUND]: 'Die angeforderte Ressource wurde nicht gefunden.'
};

// Behoben: Async-Fehler-Wrapper
const asyncHandler = (fn) => (req, res, next) => {
    Promise.resolve(fn(req, res, next)).catch(next);
};

// Behoben: Routen mit konsistenter Fehlerbehandlung
app.get('/api/users/:id', asyncHandler(async (req, res) => {
    const user = await db.getUser(req.params.id);
    if (!user) {
        throw new AppError(ErrorCategory.NOT_FOUND, 'Benutzer nicht gefunden', 404);
    }
    res.json({ success: true, data: user });
}));

app.post('/api/orders', asyncHandler(async (req, res) => {
    const order = await orderService.create(req.body);
    res.json({ success: true, data: order });
}));

// Behoben: Globaler Fehlerhandler
app.use((err, req, res, next) => {
    const errorId = err.errorId || uuidv4().substring(0, 8);
    const category = err.category || 'unexpected';

    // Behoben: Konsistente Protokollierung
    logger.error({
        errorId,
        category,
        message: err.message,
        stack: err.stack,
        path: req.path,
        method: req.method
    });

    // Behoben: Konsistentes Antwortformat
    const statusCode = err.statusCode || 500;
    const userMessage = userMessages[category] || 'Ein unerwarteter Fehler ist aufgetreten.';

    res.status(statusCode).json({
        success: false,
        errorId,
        message: userMessage
    });
});

// Behoben: Unbehandelte-Rejection-Handler
process.on('unhandledRejection', (reason, promise) => {
    logger.error({ type: 'unhandledRejection', reason });
});
<?php
// Behoben: Standardisierte PHP-Fehlerbehandlung

// Behoben: Benutzerdefinierte Ausnahmehierarchie
class AppException extends Exception {
    protected string $errorId;
    protected string $category;
    protected string $userMessage;

    public function __construct(string $category, string $message, string $userMessage) {
        $this->errorId = substr(uniqid(), 0, 8);
        $this->category = $category;
        $this->userMessage = $userMessage;
        parent::__construct($message);
    }

    public function getErrorId(): string { return $this->errorId; }
    public function getCategory(): string { return $this->category; }
    public function getUserMessage(): string { return $this->userMessage; }
}

class DatabaseException extends AppException {
    public function __construct(string $message) {
        parent::__construct('database', $message, 'Operation könnte nicht abgeschlossen werden.');
    }
}

class ValidationException extends AppException {
    public function __construct(string $message) {
        parent::__construct('validation', $message, 'Ungültige Eingabe.');
    }
}

// Behoben: Zentralisierter Fehlerhandler
class ErrorHandler {
    private static $logger;

    public static function init() {
        self::$logger = new Logger('/var/log/app/error.log');

        // Behoben: Globaler Ausnahmehandler
        set_exception_handler([self::class, 'handleException']);

        // Behoben: Globaler Fehlerhandler
        set_error_handler([self::class, 'handleError']);
    }

    public static function handleException(Throwable $e): array {
        $errorId = $e instanceof AppException ? $e->getErrorId() : substr(uniqid(), 0, 8);
        $category = $e instanceof AppException ? $e->getCategory() : 'unexpected';

        // Behoben: Konsistente Protokollierung
        self::$logger->error([
            'error_id' => $errorId,
            'category' => $category,
            'message' => $e->getMessage(),
            'file' => $e->getFile(),
            'line' => $e->getLine(),
            'trace' => $e->getTraceAsString()
        ]);

        // Behoben: Konsistente Benutzerantwort
        $userMessage = $e instanceof AppException
            ? $e->getUserMessage()
            : 'Ein unerwarteter Fehler ist aufgetreten.';

        return [
            'success' => false,
            'error_id' => $errorId,
            'message' => $userMessage
        ];
    }

    public static function handleError($severity, $message, $file, $line): bool {
        throw new ErrorException($message, 0, $severity, $file, $line);
    }
}

// Behoben: Controller mit standardisierter Fehlerbehandlung
class SichererController {

    public function createUser(array $data): array {
        try {
            $this->validateUserData($data);
            $user = $this->userService->create($data);
            return ['success' => true, 'data' => $user];
        } catch (AppException $e) {
            return ErrorHandler::handleException($e);
        }
    }

    public function deleteUser(int $id): array {
        try {
            $this->userService->delete($id);
            return ['success' => true];
        } catch (AppException $e) {
            return ErrorHandler::handleException($e);
        }
    }
}
?>

CVE-Beispiele

Keine spezifischen CVEs sind in der MITRE-Datenbank für dieses CWE gelistet. Inkonsistente Fehlerbehandlung trägt jedoch zu vielen Schwachstellen bei:

  • Informationsoffenlegung durch inkonsistente Fehlermeldungen
  • Authentifizierungsumgehungen durch unsachgemäß behandelte Fehler
  • Denial of Service durch nicht behandelte Ausnahmen

Referenzen

  1. MITRE Corporation. "CWE-544: Missing Standardized Error Handling Mechanism." https://cwe.mitre.org/data/definitions/544.html
  2. OWASP. "Error Handling Cheat Sheet."
  3. CERT. "ERR00-J. Do not suppress or ignore checked exceptions."