Ü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

AuswirkungDetails
IntegritätBereich: 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

  1. MITRE Corporation. "CWE-374: Passing Mutable Objects to an Untrusted Method." https://cwe.mitre.org/data/definitions/374.html
  2. Joshua Bloch. "Effective Java." Item 50: Make defensive copies when needed.