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

AuswirkungDetails
IntegritätBereich: 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 &current_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, &current_session, sizeof(UserSession));
    return 0;
}

// Behoben: const-Pointer für Nur-Lesen-Zugriff zurückgeben
const UserSession* get_session_readonly() {
    return &current_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

  1. MITRE Corporation. "CWE-375: Returning a Mutable Object to an Untrusted Caller." https://cwe.mitre.org/data/definitions/375.html
  2. Joshua Bloch. "Effective Java." Item 50: Make defensive copies when needed.