Rückgabe eines veränderbaren Objekts an einen nicht vertrauenswürdigen Aufrufer
Beschreibung
Rückgabe eines veränderbaren Objekts an einen nicht vertrauenswürdigen Aufrufer ist eine Schwachstelle, die auftritt, wenn Funktionen Referenzen auf veränderbare interne Daten ohne Klonen zurückgeben. Wenn nicht geklonte veränderbare Daten zurückgegeben werden, kann externer aufrufender Code diese ändern, was potenziell die internen Zustandsannahmen der Klasse verletzt und die Kapselung bricht. Diese Schwäche ist das Gegenstück zu CWE-374 (Übergabe veränderbarer Objekte an eine nicht vertrauenswürdige Methode) -- zusammen bilden sie die zwei Richtungen der Offenlegung veränderbarer Objekte.
Risiko
Die Rückgabe veränderbarer Objekte an nicht vertrauenswürdige Aufrufer ermöglicht diesen, interne Daten zu manipulieren, auf die sie keinen ändernden Zugriff haben sollten. Dies verletzt Zugriffskontroll- und Datenintegritätsprinzipien. Sicherheitsrelevante Daten wie Berechtigungssätze, Authentifizierungszustände oder Konfigurationen können von Code geändert werden, der die veränderbare Referenz erhält. Interne Invarianten, auf die die Klasse angewiesen ist, können gebrochen werden, was nachfolgende Operationen zu fehlerhaftem Verhalten oder Fehlfunktionen veranlasst. Die Änderung wird möglicherweise erst viel später erkannt, was die Fehlersuche erschwert.
Lösung
Klonen Sie alle veränderbaren Daten, bevor Sie Referenzen an externen Code zurückgeben. Deklarieren Sie zurückgegebene Daten als konstant oder unveränderlich, wo angemessen. Verwenden Sie in Java Collections.unmodifiableList() oder ähnliche Methoden. Geben Sie defensive Kopien zurück, die der Aufrufer frei ändern kann, ohne den internen Zustand zu beeinflussen. Erwägen Sie die Verwendung unveränderlicher Datenstrukturen von Anfang an. Für Arrays geben Sie eine neue Array-Kopie anstelle des Originals zurück. Dokumentieren Sie klar, wann zurückgegebene Objekte veränderliche Kopien versus Ansichten sind.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Integrität | Bereich: Integrität Daten können von Funktionen manipuliert werden, die keinen ändernden Zugriff haben sollten, was Zugriffskontroll- und Integritätsprinzipien verletzt. |
Beispielcode und Lösung
Verwundbarer Code
// Verwundbar: Veränderbares internes Objekt zurückgeben
public class VulnerableClinicalTrial {
private List<Patient> patients = new ArrayList<>();
private Date startDate;
private Map<String, String> config = new HashMap<>();
public List<Patient> getPatients() {
// Verwundbar: Gibt direkte Referenz auf interne Liste zurück
return patients;
// Aufrufer kann Patienten hinzufügen/entfernen!
}
public Date getStartDate() {
// Verwundbar: Date ist veränderbar
return startDate;
// Aufrufer kann ändern: getStartDate().setTime(0);
}
public Map<String, String> getConfig() {
// Verwundbar: Gibt interne Map zurück
return config;
}
}
// Angreifercode
VulnerableClinicalTrial trial = getTrial();
trial.getPatients().clear(); // Löscht alle Patienten!
trial.getStartDate().setTime(0); // Beschädigt Datum
trial.getConfig().put("encryption", "none"); // Deaktiviert Sicherheit
# Verwundbar: Veränderbaren internen Zustand zurückgeben
class VulnerableUserManager:
def __init__(self):
self._users = []
self._permissions = {}
def get_users(self):
# Verwundbar: Gibt interne Liste zurück
return self._users
def get_permissions(self):
# Verwundbar: Gibt internes Dict zurück
return self._permissions
# Angreifercode
manager = VulnerableUserManager()
manager.get_users().append(malicious_admin) # Unautorisierten Benutzer hinzufügen
manager.get_permissions()["admin"] = True # Rechte eskalieren
// Verwundbar: Pointer auf internen Puffer zurückgeben
typedef struct {
char password[64];
int privilege_level;
} UserSession;
static UserSession current_session;
// Verwundbar: Gibt Pointer auf interne Daten zurück
UserSession* get_current_session() {
return ¤t_session;
}
// Angreifercode
UserSession *session = get_current_session();
session->privilege_level = ADMIN_LEVEL; // Rechteeskalation
Sichere Lösung
// Behoben: Veränderbare Objekte vor der Rückgabe klonen
public class SecureClinicalTrial {
private List<Patient> patients = new ArrayList<>();
private Date startDate;
private Map<String, String> config = new HashMap<>();
public List<Patient> getPatients() {
// Behoben: Defensive Kopie zurückgeben
List<Patient> copy = new ArrayList<>();
for (Patient p : patients) {
copy.add(p.clone()); // Tiefenkopie wenn Patient veränderbar ist
}
return copy;
// Oder unveränderliche Ansicht zurückgeben:
// return Collections.unmodifiableList(patients);
}
public Date getStartDate() {
// Behoben: Klon des veränderbaren Date zurückgeben
return (Date) startDate.clone();
}
public Map<String, String> getConfig() {
// Behoben: Unveränderliche Ansicht zurückgeben
return Collections.unmodifiableMap(config);
}
}
// Besser: Unveränderliche Typen verwenden
public class ImmutableClinicalTrial {
private final List<Patient> patients;
private final Instant startDate; // Instant ist unveränderlich
private final Map<String, String> config;
public ImmutableClinicalTrial(List<Patient> patients,
Instant startDate,
Map<String, String> config) {
this.patients = List.copyOf(patients); // Unveränderliche Kopie
this.startDate = startDate;
this.config = Map.copyOf(config); // Unveränderliche Kopie
}
public List<Patient> getPatients() {
return patients; // Bereits unveränderlich
}
public Instant getStartDate() {
return startDate; // Unveränderlich
}
}
# Behoben: Kopien veränderbarer Objekte zurückgeben
import copy
class SecureUserManager:
def __init__(self):
self._users = []
self._permissions = {}
def get_users(self):
# Behoben: Tiefenkopie zurückgeben
return copy.deepcopy(self._users)
def get_permissions(self):
# Behoben: Kopie zurückgeben
return self._permissions.copy()
# Behoben: Unveränderliche Ansicht mit tuple/frozenset zurückgeben
def get_user_ids(self):
return tuple(user.id for user in self._users)
# Verwendung von dataclasses mit frozen=True für Unveränderlichkeit
from dataclasses import dataclass
from typing import Tuple
@dataclass(frozen=True)
class ImmutableUserData:
users: Tuple[str, ...] # Unveränderliches Tuple
permissions: frozenset # Unveränderliches Set
// Behoben: Kopie der Daten zurückgeben
typedef struct {
char password[64];
int privilege_level;
} UserSession;
static UserSession current_session;
// Behoben: Kopie zurückgeben, nicht Pointer
int get_current_session(UserSession *copy) {
if (copy == NULL) {
return -1;
}
// Behoben: Daten in vom Aufrufer bereitgestellten Puffer kopieren
memcpy(copy, ¤t_session, sizeof(UserSession));
return 0;
}
// Behoben: const-Pointer für Nur-Lesen-Zugriff zurückgeben
const UserSession* get_session_readonly() {
return ¤t_session;
// Compiler erzwingt Nur-Lesen
}
CVE-Beispiele
Für diese CWE sind keine spezifischen CVEs aufgeführt. Das Schwachstellenmuster tritt auf in:
- Java-Anwendungen, die interne Collections offenlegen
- APIs, die Date-Objekte oder Arrays zurückgeben
- Objektorientiertem Code mit unzureichender Kapselung
Referenzen
- MITRE Corporation. "CWE-375: Returning a Mutable Object to an Untrusted Caller." https://cwe.mitre.org/data/definitions/375.html
- Joshua Bloch. "Effective Java." Item 50: Make defensive copies when needed.