Falsche Verhaltensreihenfolge: Frühe Verstärkung

Beschreibung

Falsche Verhaltensreihenfolge: Frühe Verstärkung ist eine Schwachstelle, bei der ein Produkt einer Entität erlaubt, eine legitime aber teure Operation durchzuführen, bevor die Authentifizierung oder Autorisierung abgeschlossen wurde. Dies schafft eine Denial-of-Service-Schwachstelle, weil Angreifer ressourcenintensive Operationen auslösen können, ohne ihre Identität nachzuweisen oder entsprechende Berechtigungen zu haben. Häufige Beispiele sind das Laden von Dateien in den Speicher vor der Besitzprüfung, das Ausführen von Datenbankabfragen vor der Authentifizierung oder das Ausführen kryptographischer Operationen für nicht authentifizierte Anfragen.

Risiko

Frühe Verstärkungsschwachstellen ermöglichen Denial-of-Service-Angriffe mit minimalem Angreiferaufwand. Nicht authentifizierte Benutzer können Serverressourcen erschöpfen, indem sie wiederholt teure Operationen wie Dateilesevorgänge, Datenbankabfragen oder kryptographische Berechnungen auslösen. Da keine Authentifizierung erforderlich ist, können Angreifer unbegrenzte Anfragen von gefälschten oder rotierenden IP-Adressen machen. Die Asymmetrie zwischen billigen Angreifer-Anfragen und teuren Server-Operationen schafft einen wirtschaftlichen Vorteil für Angreifer. Systeme können abstürzen, nicht mehr reagieren oder legitimen Benutzern den Dienst verweigern, während sie bösartige Anfragen verarbeiten.

Lösung

Führen Sie immer Authentifizierungs- und Autorisierungsprüfungen durch, bevor teure Operationen ausgeführt werden. Strukturieren Sie Code so, dass Identitätsverifikation zum frühestmöglichen Zeitpunkt in der Anfrageverarbeitung erfolgt. Implementieren Sie Rate-Limiting für Vor-Authentifizierungs-Endpunkte. Verwenden Sie Lazy-Loading-Muster, die ressourcenintensive Operationen aufschieben, bis die Autorisierung bestätigt ist. Cachen Sie Authentifizierungsergebnisse, um wiederholte teure Prüfungen zu vermeiden. Entwerfen Sie APIs so, dass anonymer Zugriff auf leichtgewichtige Operationen beschränkt ist. Wenden Sie das Prinzip des schnellen Scheiterns an – weisen Sie unautorisierte Anfragen zurück, bevor Ressourcen gebunden werden.

Häufige Auswirkungen

AuswirkungDetails
VerfügbarkeitUmfang: Verfügbarkeit

DoS: Verstärkung - Angreifer können teure Operationen ohne Authentifizierung auslösen. DoS: Absturz, Beendigung oder Neustart - Systemressourcen, CPU und Speicher, können schnell verbraucht werden. Dies kann zu schlechter Systemleistung oder Systemabsturz führen.

Beispielcode

Anfälliger Code

<?php
// Anfällig: Lädt gesamte Datei vor Autorisierungsprüfung
function vulnerableDownloadFile($filename, $username) {
    // Anfällig: Teure E/A-Operation VOR Autorisierung
    $file = file_get_contents($filename);

    // Autorisierungsprüfung geschieht NACH teurer Operation
    if ($file && isOwnerOf($username, $filename)) {
        echo $file;
        return true;
    }

    return false;  // Ressourcen bereits verschwendet
}

// Angreifer kann Festplatten-E/A und Speicher erschöpfen durch Anfrage
// größer Dateien, die ihm nicht gehören - die Datei wird trotzdem geladen!
?>
# Anfällig: Datenbankabfrage vor Authentifizierung
def vulnerable_get_user_data(user_id, auth_token):
    # Anfällig: Teure Datenbankabfrage VOR Auth-Prüfung
    user_data = database.query(
        "SELECT * FROM users WHERE id = %s",
        user_id
    )

    # Teure Joins und Aggregationen
    orders = database.query(
        "SELECT * FROM orders WHERE user_id = %s",
        user_id
    )

    # Auth-Prüfung geschieht NACH Datenbankarbeit
    if not validate_token(auth_token, user_id):
        return None  # Datenbankressourcen bereits verbraucht

    return {"user": user_data, "orders": orders}
// Anfällig: Kryptographische Operation vor Authentifizierung
public class VulnerableAuthService {

    public boolean authenticate(String username, String password) {
        // Anfällig: Teure Schlüsselableitung VOR Prüfung ob Benutzer existiert
        byte[] hashedPassword = PBKDF2.hash(
            password,
            getSalt(username),  // Existiert vielleicht nicht mal
            100000  // Teure Iterationen
        );

        // Datenbankabfrage geschieht NACH teurer Krypto
        User user = userRepository.findByUsername(username);

        if (user == null) {
            return false;  // Krypto-Ressourcen verschwendet
        }

        return Arrays.equals(hashedPassword, user.getPasswordHash());
    }
}
// Anfällig: Speicherallokation vor Authentifizierung
#include <stdlib.h>

int vulnerable_process_request(int client_fd) {
    // Anfällig: Alloziert großen Puffer VOR Authentifizierung
    char *buffer = malloc(10 * 1024 * 1024);  // 10MB
    if (!buffer) return -1;

    // Potenziell großen Payload lesen
    ssize_t bytes = recv(client_fd, buffer, 10 * 1024 * 1024, 0);

    // Authentifizierung geschieht NACH Speicherallokation und E/A
    struct auth_header *auth = parse_auth_header(buffer);
    if (!validate_auth(auth)) {
        free(buffer);
        return -1;  // Speicher bereits alloziert und verwendet
    }

    process_data(buffer, bytes);
    free(buffer);
    return 0;
}

Korrigierter Code

<?php
// Korrigiert: Autorisierung VOR Laden der Datei prüfen
function secureDownloadFile($filename, $username) {
    // Korrigiert: Autorisierungsprüfung ZUERST
    if (!isOwnerOf($username, $filename)) {
        return false;  // Schnelles Scheitern, keine Ressourcen verwendet
    }

    // Teure Operation nur nach bestätigter Autorisierung
    $file = file_get_contents($filename);

    if ($file) {
        echo $file;
        return true;
    }

    return false;
}

// Alternative: Datei streamen statt vollständig zu laden
function secureStreamFile($filename, $username) {
    // Korrigiert: Autorisierung zuerst prüfen
    if (!isOwnerOf($username, $filename)) {
        http_response_code(403);
        return;
    }

    // Prüfen ob Datei existiert ohne zu laden
    if (!file_exists($filename)) {
        http_response_code(404);
        return;
    }

    // Datei streamen um Speicherverbrauch zu reduzieren
    readfile($filename);
}
?>
# Korrigiert: VOR teuren Operationen authentifizieren
def secure_get_user_data(user_id, auth_token):
    # Korrigiert: Authentifizierung ZUERST validieren
    if not validate_token(auth_token, user_id):
        raise AuthenticationError("Ungültiges Token")

    # Korrigiert: Autorisierung vor Datenzugriff prüfen
    if not is_authorized_for_user(auth_token, user_id):
        raise AuthorizationError("Zugriff verweigert")

    # Teure Operationen nur nach bestätigter Auth
    user_data = database.query(
        "SELECT * FROM users WHERE id = %s",
        user_id
    )

    orders = database.query(
        "SELECT * FROM orders WHERE user_id = %s",
        user_id
    )

    return {"user": user_data, "orders": orders}


# Korrigiert: Rate-Limit für nicht authentifizierte Endpunkte
from functools import wraps
import time

def rate_limit(max_per_minute):
    def decorator(f):
        calls = {}
        @wraps(f)
        def wrapper(*args, **kwargs):
            ip = get_client_ip()
            now = time.time()
            calls[ip] = [t for t in calls.get(ip, []) if now - t < 60]
            if len(calls[ip]) >= max_per_minute:
                raise RateLimitExceeded()
            calls[ip].append(now)
            return f(*args, **kwargs)
        return wrapper
    return decorator
// Korrigiert: Benutzerexistenz vor teurer Krypto prüfen
public class SecureAuthService {

    public boolean authenticate(String username, String password) {
        // Korrigiert: Leichtgewichtige Prüfung ZUERST
        User user = userRepository.findByUsername(username);

        if (user == null) {
            // Korrigiert: Schnelles Scheitern ohne teure Krypto
            // Kleine Verzögerung hinzufügen um Benutzer-Enumeration zu verhindern
            simulateHashDelay();
            return false;
        }

        // Korrigiert: Teure Operation nur für gültige Benutzer
        byte[] hashedPassword = PBKDF2.hash(
            password,
            user.getSalt(),
            100000
        );

        return MessageDigest.isEqual(hashedPassword, user.getPasswordHash());
    }

    private void simulateHashDelay() {
        // Timing-Angriffe verhindern während teure Krypto vermieden wird
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}
// Korrigiert: Authentifizieren bevor Ressourcen alloziert werden
#include <stdlib.h>

int secure_process_request(int client_fd) {
    // Korrigiert: Zuerst nur Auth-Header lesen (kleiner Puffer)
    char auth_buffer[256];
    ssize_t auth_bytes = recv(client_fd, auth_buffer, sizeof(auth_buffer),
                              MSG_PEEK);  // Peek ohne zu konsumieren

    // Korrigiert: Authentifizierung VOR größer Allokation validieren
    struct auth_header *auth = parse_auth_header(auth_buffer);
    if (!validate_auth(auth)) {
        return -1;  // Keine Ressourcen für ungültige Anfragen alloziert
    }

    // Korrigiert: Jetzt Ressourcen für authentifizierte Anfrage allozieren
    size_t content_length = get_content_length(auth_buffer);

    // Korrigiert: Content-Length vor Allokation validieren
    if (content_length > MAX_ALLOWED_SIZE) {
        return -1;
    }

    char *buffer = malloc(content_length);
    if (!buffer) return -1;

    // Die gepeeked Daten konsumieren und den Rest lesen
    ssize_t bytes = recv(client_fd, buffer, content_length, 0);

    process_data(buffer, bytes);
    free(buffer);
    return 0;
}

CVE-Beispiele

  • CVE-2004-2458 — Tool erstellt Verzeichnisse vor Benutzerauthentifizierung, was unauthentifizierte Verzeichniserstellung ermöglicht.

Referenzen

  1. MITRE Corporation. "CWE-408: Incorrect Behavior Order: Early Amplification." https://cwe.mitre.org/data/definitions/408.html
  2. OWASP. "Denial of Service Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Denial_of_Service_Cheat_Sheet.html