Ressourcenzuweisung ohne Limits oder Drosselung

Beschreibung

Ressourcenzuweisung ohne Limits oder Drosselung tritt auf, wenn ein Produkt eine wiederverwendbare Ressource oder Gruppe von Ressourcen im Auftrag eines Akteurs zuweist, ohne Einschränkungen für die Größe oder Anzahl der zuzuweisenden Ressourcen aufzuerlegen. Dies ermöglicht Angreifern, übermäßige Mengen an Ressourcen wie Speicher, CPU-Zyklen, Dateideskriptoren, Netzwerkverbindungen, Festplattenplatz oder Datenbankverbindungen zu verbrauchen. Ohne ordnungsgemäße Limits oder Drosselungsmechanismen kann ein einzelner bösartiger oder fehlverhaltender Client Ressourcen erschöpfen und Denial of Service für alle Benutzer des Systems verursachen.

Risiko

Ressourcenerschöpfungsangriffe gehören zu den häufigsten Denial-of-Service-Vektoren. CVE-2025-69228 in aiohttp ermöglicht Angreifern die Erschöpfung des Serverspeichers durch speziell gestaltete HTTP-POST-Anfragen. CVE-2025-66560 in Quarkus verursacht Worker-Thread-Erschöpfung, wenn Clients Verbindungen während der Antwortübertragung abbrechen. CVE-2025-48976 in Apache Commons FileUpload ermöglicht DoS durch Multipart-Header ohne Größenbeschränkungen. Diese Schwachstellen können ganze Dienste lahmlegen und in Cloud-Umgebungen auch zu erheblichen unerwarteten Kosten führen.

Lösung

Implementieren Sie strenge Ressourcenlimits auf allen Ebenen. Setzen Sie Maximalgrößen für Uploads, Request-Bodies und Allokationen. Implementieren Sie Rate Limiting pro Benutzer, IP und global. Verwenden Sie Connection Pooling mit festen Maximalgrößen. Setzen Sie Timeouts für alle Operationen. Implementieren Sie Backpressure-Mechanismen, um bei Ressourcenknappheit zu verlangsamen. Überwachen Sie den Ressourcenverbrauch und alarmieren Sie bei Anomalien. Verwenden Sie Circuit Breaker, um Kaskadenausfälle zu verhindern. Konfigurieren Sie Webserver und Frameworks mit geeigneten Limits.

Häufige Auswirkungen

AuswirkungDetails
VerfügbarkeitBereich: Denial of Service

Ressourcenerschöpfung verhindert, dass legitime Benutzer auf den Dienst zugreifen können.
FinanziellBereich: Kostenverstarkung

In Cloud-Umgebungen können Angreifer erhebliche Rechen- und Speicherkosten auslösen.
SystemstabilitätBereich: Kaskadenausfälle

Ressourcenerschöpfung in einer Komponente kann Ausfälle in abhängigen Systemen auslösen.

Beispielcode + Lösungscode

Verwundbarer Code

# VERWUNDBAR: Kein Limit für Upload-Größe
from flask import Flask, request

app = Flask(__name__)

@app.route('/upload', methods=['POST'])
def upload():
    # Kein Größenlimit - Angreifer kann Gigabytes hochladen
    file = request.files['file']
    file.save(f'/uploads/{file.filename}')
    return 'OK'

# VERWUNDBAR: Unbegrenzte Listen-Allokation
@app.route('/process', methods=['POST'])
def process():
    data = request.json
    # Kein Limit für items-Array-Größe
    items = data.get('items', [])  # Könnte Millionen von Elementen sein
    results = [process_item(item) for item in items]
    return jsonify(results)
// VERWUNDBAR: Unbegrenzte Thread-Erstellung
public class RequestHandler {
    public void handleRequest(Socket socket) {
        // Erstellt neuen Thread für jede Anfrage - kein Limit!
        new Thread(() -> processRequest(socket)).start();
    }
}

// VERWUNDBAR: Kein Speicherlimit für Datenstrukturen
public class DataCollector {
    private List<Record> records = new ArrayList<>();

    public void addRecord(Record record) {
        // Liste kann unbegrenzt wachsen
        records.add(record);
    }
}

Gefixter Code

# SICHER: Request-Größenlimits und Rate Limiting
from flask import Flask, request, jsonify
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

app = Flask(__name__)
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024  # 16 MB max

limiter = Limiter(
    app=app,
    key_func=get_remote_address,
    default_limits=["200 per day", "50 per hour"]
)

@app.route('/upload', methods=['POST'])
@limiter.limit("10 per minute")
def upload_safe():
    file = request.files.get('file')
    if not file:
        return 'Keine Datei', 400

    # Zusätzliche Größenprüfung
    file.seek(0, 2)
    size = file.tell()
    file.seek(0)

    if size > app.config['MAX_CONTENT_LENGTH']:
        return 'Datei zu groß', 413

    filename = secure_filename(file.filename)
    file.save(f'/uploads/{filename}')
    return 'OK'

# SICHER: Begrenzte Listenverarbeitung
MAX_ITEMS = 1000

@app.route('/process', methods=['POST'])
@limiter.limit("30 per minute")
def process_safe():
    data = request.json

    if not isinstance(data, dict):
        return 'Ungültige Anfrage', 400

    items = data.get('items', [])

    if len(items) > MAX_ITEMS:
        return f'Zu viele Elemente (max {MAX_ITEMS})', 400

    results = [process_item(item) for item in items]
    return jsonify(results)
// SICHER: Thread-Pool mit fester Größe
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

public class SecureRequestHandler {
    private static final int MAX_THREADS = 100;
    private static final int MAX_PENDING = 1000;

    private final ExecutorService executor = Executors.newFixedThreadPool(MAX_THREADS);
    private final Semaphore semaphore = new Semaphore(MAX_PENDING);

    public void handleRequest(Socket socket) throws InterruptedException {
        // Ausstehende Anfragen begrenzen
        if (!semaphore.tryAcquire(5, TimeUnit.SECONDS)) {
            socket.close();
            throw new RejectedExecutionException("Zu viele ausstehende Anfragen");
        }

        executor.submit(() -> {
            try {
                processRequest(socket);
            } finally {
                semaphore.release();
            }
        });
    }
}

// SICHER: Begrenzte Datenstruktur
public class BoundedDataCollector {
    private static final int MAX_RECORDS = 10000;
    private final List<Record> records = new ArrayList<>();

    public synchronized void addRecord(Record record) throws CapacityExceededException {
        if (records.size() >= MAX_RECORDS) {
            throw new CapacityExceededException("Maximale Datensätze erreicht");
        }
        records.add(record);
    }
}

Ausgenutzt in der Praxis

aiohttp Speichererschöpfung (aiohttp, 2025)

CVE-2025-69228 in aiohttp 3.13.2 und früher ermöglicht Angreifern das Senden speziell gestalteter HTTP-POST-Anfragen, die unbegrenzte Speicherallokation verursachen, was zu Server-Abstürzen und Denial of Service für asynchrone Python-Webanwendungen führt.

Quarkus Worker-Thread-Erschöpfung (Quarkus, 2025)

CVE-2025-66560 in Quarkus vor 3.31.0 verursacht, dass Worker-Threads unendlich blockieren, wenn Client-Verbindungen während der HTTP-Antwort-Übertragung abbrechen, was zu Thread-Pool-Erschöpfung und vollständigem Service-Ausfall führt.


Tools zum Testen/Ausnutzen

  • slowloris - Slow HTTP Denial of Service Tool.

  • wrk - HTTP-Benchmarking und Lasttests.

  • vegeta - HTTP-Lasttesttool.


CVE-Beispiele


Referenzen

  1. MITRE. "CWE-770: Allocation of Resources Without Limits or Throttling." https://cwe.mitre.org/data/definitions/770.html

  2. OWASP. "Denial of Service Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Denial_of_Service_Cheat_Sheet.html