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
| Auswirkung | Details |
|---|---|
| Verfügbarkeit | Umfang: 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
- MITRE Corporation. "CWE-408: Incorrect Behavior Order: Early Amplification." https://cwe.mitre.org/data/definitions/408.html
- OWASP. "Denial of Service Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Denial_of_Service_Cheat_Sheet.html