Unzureichender Ressourcenpool
Beschreibung
Unzureichender Ressourcenpool ist eine Schwachstelle, die auftritt, wenn der Ressourcenpool eines Systems die Spitzennachfrage nicht bedienen kann, was Angreifern ermöglicht, verfügbare Ressourcen durch eine große Anzahl von Anfragen zu erschöpfen und dadurch legitime Benutzer zu blockieren. Der Ressourcenpool (wie Datenbankverbindungen, Threads, Datei-Handles oder Sockets) ist für die erwartete Last zu klein dimensioniert oder hat keinen Schutz gegen absichtliche Erschöpfung. Häufig ist die Konsequenz eine "Flut" von Verbindungen oder Sitzungen, die das System daran hindert, legitime Benutzer zu bedienen.
Risiko
Unzureichende Ressourcenpools ermöglichen Denial-of-Service-Angriffe, bei denen Angreifer alle verfügbaren Ressourcen verbrauchen. Erschöpfung des Datenbankverbindungspools verhindert, dass Anwendungen Abfragen verarbeiten. Thread-Pool-Erschöpfung verursacht Request-Timeouts und Anwendungs-Hänger. Datei-Handle-Erschöpfung kann Anwendungen oder Betriebssysteme zum Absturz bringen. Socket-Erschöpfung verhindert neue Netzwerkverbindungen. Diese Angriffe sind besonders effektiv, wenn Ressourcen an nicht authentifizierte Benutzer vergeben werden oder wenn es keine Limits pro Client gibt. Die Angriffe können mit minimalen Angreiferressourcen durchgeführt werden und erhebliche Dienststörungen verursachen.
Lösung
Vermeiden Sie ressourcenintensive Operationen für nicht authentifizierte oder ungültige Anfragen. Implementieren Sie Geschwindigkeitsprüfungen, um missbräuchliches Verhalten zu erkennen und zu blockieren. Wenden Sie Rate-Limiting pro Client/IP-Adresse an. Verwenden Sie Load-Balancing zur Verkehrsverteilung. Schließen Sie Ressourcen-Handles ordnungsgemäß, wenn sie nicht mehr benötigt werden. Setzen Sie angemessene Timeouts für Ressourcen-Akquisition. Implementieren Sie Verbindungslimits pro Benutzer oder Quelle. Schützen Sie ressourcenintensive Operationen mit Authentifizierung und Autorisierung. Überwachen Sie die Ressourcenpool-Auslastung und alarmieren Sie bei Anomalien. Dimensionieren Sie Pools angemessen für erwartete Spitzenlast mit Sicherheitsmarge.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Verfügbarkeit | Umfang: Verfügbarkeit Denial of Service - Absturz, Beendigung, Neustart oder andere Verfügbarkeitsprobleme. Ressourcenfluten lösen oft Abstürze über reine Ressourcenverweigerung hinaus aus und repräsentieren möglicherweise zusätzliche Schwachstellen. |
| Integrität | Umfang: Integrität Unerwartete Zustandsänderungen können auftreten, wenn Systeme unter Ressourcendruck stehen. |
Beispielcode
Anfälliger Code
// Anfällig: Kleiner Verbindungspool leicht erschöpfbar
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
public class VulnerableConnectionPool {
public DataSource createDataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setUrl("jdbc:mysql://localhost:3306/mydb");
ds.setUsername("user");
ds.setPassword("password");
// Anfällig: Nur 5 Verbindungen - leicht erschöpfbar
ds.setMaxTotal(5);
ds.setMaxIdle(5);
// Anfällig: Länge Wartezeit - Anfragen stauen sich
ds.setMaxWaitMillis(60000);
return ds;
}
}
// Anfällig: Keine Verbindungsfreigabe
public class VulnerableService {
public void processRequest(DataSource ds) throws SQLException {
Connection conn = ds.getConnection(); // Holt Verbindung
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users");
// Ergebnisse verarbeiten...
// Anfällig: Verbindung nie geschlossen!
// Pool-Erschöpfung über Zeit
}
}
# Anfällig: Fester Thread-Pool leicht erschöpfbar
from concurrent.futures import ThreadPoolExecutor
import time
# Anfällig: Nur 4 Threads verfügbar
executor = ThreadPoolExecutor(max_workers=4)
def vulnerable_handle_request(request):
# Anfällig: Lang laufende Operation blockiert Thread
# Angreifer kann alle Threads mit langsamen Anfragen belegen
time.sleep(30) # Simuliert langsame Operation
return process(request)
def vulnerable_server():
while True:
request = accept_connection()
# Anfällig: Kein Limit für ausstehende Anfragen
# Anfällig: Kein Timeout für langsame Clients
executor.submit(vulnerable_handle_request, request)
// Anfällig: Dateideskriptor-Limit nicht geprüft
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int vulnerable_server(int listen_fd) {
while (1) {
// Anfällig: Keine Prüfung der Anzahl offener Verbindungen
int client_fd = accept(listen_fd, NULL, NULL);
if (client_fd >= 0) {
// Anfällig: Hält Verbindung offen
// Kein Limit für gleichzeitige Verbindungen
handle_client(client_fd);
// Anfällig: Schließt client_fd nie
}
}
return 0;
}
Korrigierter Code
// Korrigiert: Ordnungsgemäß dimensionierter Pool mit Schutz
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
public class SecureConnectionPool {
public DataSource createDataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setUrl("jdbc:mysql://localhost:3306/mydb");
ds.setUsername("user");
ds.setPassword("password");
// Korrigiert: Angemessen dimensionierter Pool
ds.setMaxTotal(50); // Vernünftige max Verbindungen
ds.setMaxIdle(20); // Einige idle Verbindungen behalten
ds.setMinIdle(5); // Minimale idle Verbindungen
// Korrigiert: Kurze Wartezeit mit Timeout
ds.setMaxWaitMillis(5000); // 5 Sekunden Timeout
// Korrigiert: Validierung und Eviction
ds.setTestOnBorrow(true);
ds.setValidationQuery("SELECT 1");
ds.setTimeBetweenEvictionRunsMillis(30000);
ds.setMinEvictableIdleTimeMillis(60000);
// Korrigiert: Aufgegebene Verbindungen entfernen
ds.setRemoveAbandonedOnBorrow(true);
ds.setRemoveAbandonedTimeout(60); // 60 Sekunden
return ds;
}
}
// Korrigiert: Verbindungen immer schließen
public class SecureService {
public void processRequest(DataSource ds) throws SQLException {
// Korrigiert: Try-with-resources stellt Schließung sicher
try (Connection conn = ds.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users")) {
// Ergebnisse verarbeiten...
} // Verbindung automatisch geschlossen
}
}
# Korrigiert: Begrenzte Queue und pro-Client-Limits
from concurrent.futures import ThreadPoolExecutor
import time
from collections import defaultdict
import threading
# Korrigiert: Vernünftige Pool-Größe mit begrenzter Queue
executor = ThreadPoolExecutor(max_workers=50)
# Korrigiert: Pro-Client-Verbindungen verfolgen
client_requests = defaultdict(int)
client_lock = threading.Lock()
MAX_PER_CLIENT = 5
def secure_handle_request(client_id, request):
try:
# Korrigiert: Timeout für Operationen
with timeout(10): # 10 Sekunden max
return process(request)
finally:
# Korrigiert: Zähler bei Fertigstellung dekrementieren
with client_lock:
client_requests[client_id] -= 1
def secure_server():
while True:
request, client_id = accept_connection()
# Korrigiert: Pro-Client Rate-Limiting
with client_lock:
if client_requests[client_id] >= MAX_PER_CLIENT:
reject_connection(request, "Zu viele gleichzeitige Anfragen")
continue
client_requests[client_id] += 1
try:
# Korrigiert: Submit mit begrenzter Queue verwenden
future = executor.submit(secure_handle_request, client_id, request)
# Queue ist durch internes Executor-Limit begrenzt
except Exception:
with client_lock:
client_requests[client_id] -= 1
reject_connection(request, "Server überlastet")
// Korrigiert: Verbindungen verfolgen und limitieren
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#define MAX_CONNECTIONS 100
static int active_connections = 0;
static pthread_mutex_t conn_mutex = PTHREAD_MUTEX_INITIALIZER;
int secure_server(int listen_fd) {
while (1) {
int client_fd = accept(listen_fd, NULL, NULL);
if (client_fd < 0) {
continue;
}
// Korrigiert: Verbindungslimit prüfen
pthread_mutex_lock(&conn_mutex);
if (active_connections >= MAX_CONNECTIONS) {
pthread_mutex_unlock(&conn_mutex);
// Korrigiert: Überschüssige Verbindungen ablehnen
const char *msg = "Server beschäftigt\n";
write(client_fd, msg, strlen(msg));
close(client_fd);
continue;
}
active_connections++;
pthread_mutex_unlock(&conn_mutex);
// Korrigiert: In Thread mit Cleanup behandeln
pthread_t thread;
int *fd_ptr = malloc(sizeof(int));
*fd_ptr = client_fd;
pthread_create(&thread, NULL, handle_client_thread, fd_ptr);
pthread_detach(thread);
}
return 0;
}
void *handle_client_thread(void *arg) {
int client_fd = *(int *)arg;
free(arg);
// Socket-Timeout setzen
struct timeval tv = {.tv_sec = 30, .tv_usec = 0};
setsockopt(client_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
// Client behandeln...
handle_client(client_fd);
// Korrigiert: Immer schließen und dekrementieren
close(client_fd);
pthread_mutex_lock(&conn_mutex);
active_connections--;
pthread_mutex_unlock(&conn_mutex);
return NULL;
}
CVE-Beispiele
- CVE-1999-1363 — Datei-Lock-Erschöpfung verursacht Absturz.
- CVE-2001-1340 — Einzelverbindung ohne erzwungene Trennung nicht authentifizierter Benutzer.
- CVE-2002-0406 — Verbindungserschöpfung über nicht authentifizierte Anfragen.
Referenzen
- MITRE Corporation. "CWE-410: Insufficient Resource Pool." https://cwe.mitre.org/data/definitions/410.html
- OWASP. "Denial of Service Cheat Sheet." https://cheatsheetseries.owasp.org/