Übergabe veränderbarer Objekte an eine nicht vertrauenswürdige Methode
Beschreibung
Übergabe veränderbarer Objekte an eine nicht vertrauenswürdige Methode ist eine Schwachstelle, die auftritt, wenn ein Programm nicht geklonte veränderbare Daten als Argument an eine Methode oder Funktion übergibt. Da die aufgerufene Funktion diese Daten ändern oder löschen kann, kann sie Annahmen verletzen, die der Aufrufer über den Programmzustand getroffen hat. Wenn nicht verifizierter oder nicht vertrauenswürdiger Code Referenzen auf veränderbare Objekte erhält, könnte er diese unerwartet modifizieren und dadurch diese Daten im ursprünglichen Ausführungskontext ungültig machen. Dies ist besonders problematisch, wenn das veränderbare Objekt sicherheitsrelevante Daten, internen Zustand oder Daten enthält, von denen andere Teile des Programms annehmen, dass sie konstant sind.
Risiko
Die Übergabe veränderbarer Objekte an nicht vertrauenswürdige Methoden kann zu Datenbeschädigung, Sicherheitsumgehungen und Rechteeskalation führen. Eine nicht vertrauenswürdige Methode könnte Authentifizierungstokens, Berechtigungssätze oder Konfigurationsdaten ändern. Interner Zustand, den der Aufrufer als unverändert annimmt, kann geändert werden, was nachfolgende Operationen zu fehlerhaftem Verhalten veranlasst. In Multithreading-Umgebungen könnten die Änderungen zu jeder Zeit auftreten und Race Conditions erzeugen. Das Risiko wird verstärkt, wenn das veränderbare Objekt über mehrere Komponenten hinweg geteilt wird -- Änderungen durch eine nicht vertrauenswürdige Methode betreffen alle Komponenten, die dieses Objekt verwenden.
Lösung
Übergeben Sie Daten als Konstanten oder unveränderbare Objekte, wenn möglich. Klonen Sie alle veränderbaren Daten, bevor Sie sie an externe oder nicht vertrauenswürdige Funktionen übergeben. Verwenden Sie defensives Kopieren sowohl für Eingabeparameter als auch für Rückgabewerte. Erwägen Sie die Verwendung unvereinderlicher Collections und Objekte. In Java verwenden Sie Collections.unmodifiableList() oder ähnliche Methoden. Markieren Sie Felder als final, wo angemessen. Beim Klonen stellen Sie sicher, dass Tiefenkopien für Objekte erstellt werden, die andere veränderbare Objekte enthalten. Validieren Sie, dass zurückgegebene Daten nicht unerwartet geändert wurden, wenn die veränderbare Referenz geteilt wurde.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Integrität | Bereich: Integrität Daten könnten von einer anderen Funktion manipuliert werden, die nicht die Berechtigung haben sollte, sie zu ändern. |
Beispielcode und Lösung
Verwundbarer Code
// Verwundbar: Veränderbares Objekt ohne Klonen übergeben
public class VulnerableBookStore {
private List<Book> inventory = new ArrayList<>();
public void addBook(Book book) {
inventory.add(book);
// Verwundbar: Veränderbares book an externen Service übergeben
// Externer Service könnte das Buch ändern
externalAuditService.logNewBook(book);
// Buch im Inventar hat nun möglicherweise andere Werte!
}
public List<Book> getInventory() {
// Verwundbar: Veränderbare interne Liste zurückgeben
return inventory;
// Aufrufer kann internen Zustand ändern
}
}
public class Book {
private String title;
private double price;
// Getter und Setter - Objekt ist veränderbar
}
# Verwundbar: Veränderbare Standardargumente und geteilter Zustand
class VulnerableConfig:
def __init__(self, settings={}): # Verwundbar: veränderbarer Standardwert
self.settings = settings
def get_settings(self):
# Verwundbar: Gibt Referenz auf internen Zustand zurück
return self.settings
def vulnerable_process(data_list):
# Verwundbar: Die übergebene Liste ändern
plugin.process(data_list) # Plugin kann Liste ändern!
# Originalliste des Aufrufers ist nun geändert
// Verwundbar: Pointer auf veränderbare Daten übergeben
typedef struct {
char *username;
int permissions;
} UserContext;
void vulnerable_log_action(UserContext *ctx, const char *action) {
// Verwundbar: Veränderbaren Pointer an Logger übergeben
// Logger könnte ctx ändern
external_logger_log(ctx, action);
// ctx->permissions könnte geändert worden sein!
if (ctx->permissions & ADMIN_PERMISSION) {
perform_admin_action(); // Könnte unerwartet ausgeführt werden
}
}
Sichere Lösung
// Behoben: Veränderbare Objekte vor Übergabe klonen
public class SecureBookStore {
private List<Book> inventory = new ArrayList<>();
public void addBook(Book book) {
// Behoben: Buch vor Speichern und Übergeben klonen
Book bookCopy = book.clone();
inventory.add(bookCopy);
// Behoben: Klon an externen Service übergeben
externalAuditService.logNewBook(book.clone());
// Internes Inventar ist geschützt
}
public List<Book> getInventory() {
// Behoben: Defensive Kopie zurückgeben
List<Book> copy = new ArrayList<>();
for (Book book : inventory) {
copy.add(book.clone());
}
return copy;
// Oder unveränderbare Hülle verwenden:
// return Collections.unmodifiableList(inventory);
}
}
// Behoben: Unveränderliche Book-Klasse
public final class ImmutableBook {
private final String title;
private final double price;
public ImmutableBook(String title, double price) {
this.title = title;
this.price = price;
}
public String getTitle() { return title; }
public double getPrice() { return price; }
// Keine Setter - Objekt ist unveränderlich
}
# Behoben: Defensives Kopieren
import copy
class SecureConfig:
def __init__(self, settings=None):
# Behoben: Keinen veränderbaren Standardwert verwenden
self.settings = copy.deepcopy(settings) if settings else {}
def get_settings(self):
# Behoben: Kopie des internen Zustands zurückgeben
return copy.deepcopy(self.settings)
def secure_process(data_list):
# Behoben: Kopie an Plugin übergeben
data_copy = copy.deepcopy(data_list)
plugin.process(data_copy)
# Originalliste ist unverändert
# Behoben: Verwendung unveränderlicher Strukturen
from dataclasses import dataclass
from typing import FrozenSet
@dataclass(frozen=True) # Unveränderlich
class ImmutableConfig:
name: str
values: tuple # Unveränderliche Sequenz
def secure_process_immutable(config: ImmutableConfig):
# Config kann nicht geändert werden
plugin.process(config)
// Behoben: Kopie der Daten übergeben
typedef struct {
char username[64]; // Feste Größe, kann kopiert werden
int permissions;
} UserContext;
void secure_log_action(const UserContext *ctx, const char *action) {
// Behoben: Kopie für Logger erstellen
UserContext ctx_copy;
memcpy(&ctx_copy, ctx, sizeof(UserContext));
external_logger_log(&ctx_copy, action);
// Originales ctx ist unverändert
if (ctx->permissions & ADMIN_PERMISSION) {
perform_admin_action();
}
}
// Behoben: const verwenden um Nur-Lesen-Absicht anzuzeigen
void secure_read_only(const UserContext *ctx) {
// Behoben: Compiler erzwingt Nur-Lesen-Zugriff
// ctx->permissions = 0; // Würde Compilerfehler verursachen
log_user(ctx->username);
}
CVE-Beispiele
Für diese CWE sind keine spezifischen CVEs aufgeführt. Das Schwachstellenmuster tritt auf in:
- Java-Anwendungen, die Collections an Plugins übergeben
- Python-Code mit veränderbaren Standardargumenten
- APIs, die vom Aufrufer bereitgestellte Objekte modifizieren
Referenzen
- MITRE Corporation. "CWE-374: Passing Mutable Objects to an Untrusted Method." https://cwe.mitre.org/data/definitions/374.html
- Joshua Bloch. "Effective Java." Item 50: Make defensive copies when needed.