Methode mit Zugriff auf Mitgliedselement einer anderen Klasse

Beschreibung

Methode mit Zugriff auf Mitgliedselement einer anderen Klasse tritt auf, wenn eine Methode einer Klasse eine Operation durchführt, die direkt auf ein Mitgliedselement einer anderen Klasse zugreift. Dies verletzt das Prinzip der Kapselung in der objektorientierten Programmierung, bei dem Klassen ihren internen Zustand verbergen und kontrollierten Zugriff über Methoden bereitstellen sollten. Wenn eine Klasse direkt auf die Felder einer anderen Klasse zugreift, entsteht enge Kopplung, Informationsverbergung wird gebrochen, und der Code wird schwerer zu warten und zu sichern.

Risiko

Direkter Zugriff auf Mitglieder einer anderen Klasse hat indirekte Sicherheitsimplikationen. Umgehung von Accessor-Methoden bedeutet Umgehung jeglicher Validierung, Autorisierung oder Audit-Protokollierung in diesen Methoden. Enge Kopplung macht Sicherheits-Refactoring schwierig. Änderungen an der internen Repräsentierung einer Klasse brechen abhängige Klassen. Der exponierte interne Zustand kann auf unbeabsichtigte Weise manipuliert werden. Sicherheitskontrollen, die in Accessor-Methoden zentralisiert sein sollten, sind verstreut oder fehlen. Code-Review auf Sicherheit wird mit unklaren Grenzen zwischen Klassen schwieriger.

Lösung

Verwenden Sie Accessor-Methoden (Getter/Setter) anstelle von direktem Feldzugriff. Wenden Sie ordnungsgemäße Zugriffsmodifikatoren (private, protected) auf Mitgliedsvariablen an. Befolgen Sie das Law of Demeter - greifen Sie nicht durch Objekte durch. Verwenden Sie Interfaces, um Verträge zwischen Klassen zu definieren. Wenden Sie das Tell-Don't-Ask-Prinzip an - sagen Sie Objekten, was sie tun sollen, anstatt ihren Zustand abzufragen. Erwägen Sie wo angemessen die Verwendung unveränderlicher Objekte. Verwenden Sie statische Analysetools, um Kapselungsverletzungen zu erkennen. Definieren Sie klare Klassenverantwortlichkeiten, um Inter-Klassen-Abhängigkeiten zu minimieren.

Häufige Auswirkungen

AuswirkungDetails
AndereBereich: Ändere

Reduzierte Wartbarkeit - Enge Kopplung macht Code schwerer sicher zu ändern.
AndereBereich: Ändere

Erhöhte analytische Komplexität - Sicherheitsanalyse ist mit unklaren Grenzen schwieriger.
IntegritätBereich: Integrität

Schutzmechanismus umgehen - Validierung und Zugriffskontrolle in Accessors wird umgangen.

Beispielcode

Anfälliger Code

// Anfällig: Direkter Zugriff auf Mitglieder einer anderen Klasse
public class VulnerableBankAccount {
    // Anfällig: Öffentliches Feld - keine Kapselung!
    public double balance;
    public String accountNumber;
    public String ownerName;
    public boolean isActive;
}

public class VulnerableTransactionProcessor {

    public void processWithdrawal(VulnerableBankAccount account, double amount) {
        // Anfällig: Direkter Feldzugriff von einer anderen Klasse
        if (account.balance >= amount) {
            account.balance -= amount;  // Direkte Modifikation!
        }
        // Keine Validierung, keine Audit-Protokollierung, keine Autorisierungsprüfung
    }

    public void transferFunds(VulnerableBankAccount from,
                             VulnerableBankAccount to,
                             double amount) {
        // Anfällig: Direkter Zugriff auf Felder
        if (from.isActive && to.isActive) {  // Direkter Feldzugriff
            if (from.balance >= amount) {    // Direkter Feldzugriff
                from.balance -= amount;      // Direkte Modifikation!
                to.balance += amount;        // Direkte Modifikation!
            }
        }
        // Umgeht jegliche Validierung, die in der BankAccount-Klasse existieren sollte
    }

    public void closeAccount(VulnerableBankAccount account) {
        // Anfällig: Direkte Zustandsmodifikation
        account.isActive = false;      // Keine Autorisierungsprüfung!
        account.balance = 0;           // Potenzieller Datenverlust!
        account.ownerName = "CLOSED";  // Direkte Modifikation!
    }
}

// Angriffsszenario:
// VulnerableBankAccount account = new VulnerableBankAccount();
// account.balance = 1000000;  // Jeder kann beliebigen Saldo setzen!
// account.isActive = true;    // Kontoaktivierungsprozess umgehen!
# Anfällig: Direkter Attributzugriff in Python
class VulnerableUser:
    def __init__(self, username, email):
        # Anfällig: Alle Attribute öffentlich zugänglich
        self.username = username
        self.email = email
        self.password_hash = None
        self.role = 'user'
        self.is_admin = False
        self.login_attempts = 0
        self.locked = False


class VulnerableAuthService:

    def authenticate(self, user, password):
        # Anfällig: Direkter Zugriff auf internen Zustand von User
        if user.locked:  # Direkter Feldzugriff
            return False

        if verify_password(password, user.password_hash):  # Direkter Zugriff
            user.login_attempts = 0  # Direkte Modifikation
            return True
        else:
            user.login_attempts += 1  # Direkte Modifikation
            if user.login_attempts >= 3:  # Direkter Zugriff
                user.locked = True  # Direkte Modifikation
            return False

    def promote_to_admin(self, user):
        # Anfällig: Direkte Modifikation von sicherheitskritischem Feld
        user.is_admin = True  # Keine Autorisierungsprüfung!
        user.role = 'admin'   # Direkte Modifikation!

    def reset_user(self, user):
        # Anfällig: Manipuliert internen Zustand direkt
        user.login_attempts = 0
        user.locked = False
        user.password_hash = None  # Gefährlich!


# Angriff:
# user = VulnerableUser("attacker", "[email protected]")
# user.is_admin = True  # Privilegieneskalation!
# user.role = 'admin'   # Direkte Rollenzuweisung!
// Anfällig: Direkter Feldzugriff in C#
public class VulnerableOrder
{
    // Anfällig: Öffentliche Felder statt Properties
    public decimal TotalAmount;
    public string Status;
    public List<OrderItem> Items;
    public DateTime CreatedAt;
    public bool IsPaid;
}

public class VulnerableOrderProcessor
{
    public void ApplyDiscount(VulnerableOrder order, decimal discountPercent)
    {
        // Anfällig: Direkter Feldzugriff und Modifikation
        decimal discount = order.TotalAmount * (discountPercent / 100);
        order.TotalAmount -= discount;  // Keine Validierung!
        // Könnte TotalAmount negativ setzen mit großem Rabatt
    }

    public void ProcessPayment(VulnerableOrder order)
    {
        // Anfällig: Direkte Zustandsmanipulation
        order.IsPaid = true;  // Keine Zahlungsverifizierung!
        order.Status = "PAID";
    }

    public void AddItem(VulnerableOrder order, OrderItem item)
    {
        // Anfällig: Direkter Collection-Zugriff
        order.Items.Add(item);  // Umgeht jegliche Bestellpostenvalidierung
        // Direkt Total modifizieren - keine Neuberechnung
        order.TotalAmount += item.Price;
    }

    public void ClearOrder(VulnerableOrder order)
    {
        // Anfällig: Direkte Manipulation aller Felder
        order.Items.Clear();
        order.TotalAmount = 0;
        order.Status = "CLEARED";
        order.IsPaid = false;
    }
}

Korrigierter Code

// Korrigiert: Ordnungsgemäße Kapselung mit kontrolliertem Zugriff
public class FixedBankAccount {
    // Private Felder - nicht direkt zugänglich
    private double balance;
    private String accountNumber;
    private String ownerName;
    private boolean isActive;
    private final AuditLogger auditLogger;

    public FixedBankAccount(String accountNumber, String ownerName,
                            AuditLogger auditLogger) {
        this.accountNumber = accountNumber;
        this.ownerName = ownerName;
        this.balance = 0;
        this.isActive = false;
        this.auditLogger = auditLogger;
    }

    // Kontrollierter Zugriff mit Validierung
    public double getBalance() {
        return balance;
    }

    public String getAccountNumber() {
        return accountNumber;
    }

    public boolean isActive() {
        return isActive;
    }

    // Geschäftsoperationen in der Klasse gekapselt
    public void withdraw(double amount) throws InsufficientFundsException {
        validateActive();
        validateAmount(amount);

        if (balance < amount) {
            throw new InsufficientFundsException("Ungenügender Saldo");
        }

        balance -= amount;
        auditLogger.log("Abhebung", accountNumber, amount);
    }

    public void deposit(double amount) {
        validateActive();
        validateAmount(amount);

        balance += amount;
        auditLogger.log("Einzahlung", accountNumber, amount);
    }

    public void activate(String authorizedBy) {
        if (isActive) {
            throw new IllegalStateException("Konto bereits aktiv");
        }
        isActive = true;
        auditLogger.log("Konto aktiviert durch " + authorizedBy, accountNumber, 0);
    }

    public void deactivate(String authorizedBy, String reason) {
        if (!isActive) {
            throw new IllegalStateException("Konto bereits inaktiv");
        }
        isActive = false;
        auditLogger.log("Konto deaktiviert: " + reason, accountNumber, balance);
    }

    private void validateActive() {
        if (!isActive) {
            throw new IllegalStateException("Konto ist nicht aktiv");
        }
    }

    private void validateAmount(double amount) {
        if (amount <= 0) {
            throw new IllegalArgumentException("Betrag muss positiv sein");
        }
    }
}

// Korrigiert: TransactionProcessor verwendet Methoden, keinen Feldzugriff
public class FixedTransactionProcessor {

    private final AuthorizationService authService;

    public FixedTransactionProcessor(AuthorizationService authService) {
        this.authService = authService;
    }

    public void processWithdrawal(FixedBankAccount account, double amount,
                                  User requestedBy) throws TransactionException {
        // Autorisierung prüfen
        authService.checkPermission(requestedBy, "WITHDRAW", account);

        // An Konto delegieren - alle Logik dort gekapselt
        account.withdraw(amount);
    }

    public void transferFunds(FixedBankAccount from, FixedBankAccount to,
                             double amount, User requestedBy) throws TransactionException {
        // Autorisierungsprüfung
        authService.checkPermission(requestedBy, "TRANSFER", from);

        // Kontomethoden verwenden - kein direkter Feldzugriff
        from.withdraw(amount);
        to.deposit(amount);
    }
}
# Korrigiert: Ordnungsgemäße Kapselung in Python
from dataclasses import dataclass, field
from typing import Optional
import hashlib
import secrets


class FixedUser:
    """Benutzer mit ordnungsgemäßer Kapselung."""

    def __init__(self, username: str, email: str):
        self._username = username
        self._email = email
        self._password_hash: Optional[str] = None
        self._role = 'user'
        self._is_admin = False
        self._login_attempts = 0
        self._locked = False

    # Nur-Lese-Properties
    @property
    def username(self) -> str:
        return self._username

    @property
    def email(self) -> str:
        return self._email

    @property
    def is_locked(self) -> bool:
        return self._locked

    @property
    def is_admin(self) -> bool:
        return self._is_admin

    # Kontrollierte Operationen
    def set_password(self, password: str) -> None:
        """Passwort mit ordnungsgemäßem Hashing setzen."""
        if len(password) < 8:
            raise ValueError("Passwort muss mindestens 8 Zeichen haben")
        salt = secrets.token_hex(16)
        self._password_hash = self._hash_password(password, salt)

    def verify_password(self, password: str) -> bool:
        """Passwort verifizieren und Login-Versuche handhaben."""
        if self._locked:
            raise AccountLockedException("Konto ist gesperrt")

        if self._password_hash is None:
            return False

        if self._verify_hash(password, self._password_hash):
            self._login_attempts = 0
            return True
        else:
            self._record_failed_attempt()
            return False

    def _record_failed_attempt(self) -> None:
        """Intern: Fehlgeschlagenen Login-Versuch aufzeichnen."""
        self._login_attempts += 1
        if self._login_attempts >= 3:
            self._locked = True

    def unlock(self, admin_user: 'FixedUser') -> None:
        """Konto entsperren - erfordert Admin."""
        if not admin_user.is_admin:
            raise PermissionError("Nur Admins können Konten entsperren")
        self._locked = False
        self._login_attempts = 0

    def promote_to_admin(self, super_admin: 'FixedUser') -> None:
        """Zum Admin befördern - erfordert Super-Admin."""
        if not super_admin.is_admin:
            raise PermissionError("Nur Admins können Benutzer befördern")
        self._is_admin = True
        self._role = 'admin'


class FixedAuthService:
    """Authentifizierungsdienst mit ordnungsgemäßer Kapselung."""

    def __init__(self, audit_logger):
        self._audit = audit_logger

    def authenticate(self, user: FixedUser, password: str) -> bool:
        """Benutzer über ordnungsgemäße Methoden authentifizieren."""
        try:
            # Methode des Benutzers verwenden - keinen Zugriff auf internen Zustand
            if user.verify_password(password):
                self._audit.log(f"Benutzer {user.username} authentifiziert")
                return True
            else:
                self._audit.log(f"Fehlgeschlagener Auth-Versuch für {user.username}")
                return False
        except AccountLockedException:
            self._audit.log(f"Auth blockiert - {user.username} ist gesperrt")
            return False
// Korrigiert: Ordnungsgemäße Kapselung in C#
public class FixedOrder
{
    // Private Felder
    private decimal _totalAmount;
    private string _status;
    private readonly List<OrderItem> _items;
    private bool _isPaid;

    public FixedOrder()
    {
        _items = new List<OrderItem>();
        _status = "PENDING";
        _totalAmount = 0;
        _isPaid = false;
    }

    // Nur-Lese-Properties
    public decimal TotalAmount => _totalAmount;
    public string Status => _status;
    public IReadOnlyList<OrderItem> Items => _items.AsReadOnly();
    public bool IsPaid => _isPaid;

    // Geschäftsmethoden mit Validierung
    public void AddItem(OrderItem item)
    {
        if (item == null)
            throw new ArgumentNullException(nameof(item));

        if (_status != "PENDING")
            throw new InvalidOperationException("Kann nicht ausstehende Bestellung nicht ändern");

        item.Validate();  // Artikel validiert sich selbst
        _items.Add(item);
        RecalculateTotal();
    }

    public void RemoveItem(OrderItem item)
    {
        if (_status != "PENDING")
            throw new InvalidOperationException("Kann nicht ausstehende Bestellung nicht ändern");

        _items.Remove(item);
        RecalculateTotal();
    }

    public void ApplyDiscount(decimal discountPercent, string authorizedBy)
    {
        if (discountPercent < 0 || discountPercent > 50)
            throw new ArgumentException("Rabatt muss zwischen 0 und 50% liegen");

        decimal discount = _totalAmount * (discountPercent / 100);
        _totalAmount -= discount;

        // Rabatt protokollieren
        AuditLog.Record($"Rabatt von {discountPercent}% angewendet durch {authorizedBy}");
    }

    public void MarkAsPaid(PaymentConfirmation confirmation)
    {
        if (confirmation == null)
            throw new ArgumentNullException(nameof(confirmation));

        if (!confirmation.IsValid)
            throw new InvalidOperationException("Ungültige Zahlungsbestätigung");

        if (confirmation.Amount < _totalAmount)
            throw new InvalidOperationException("Zahlungsbetrag ungenügend");

        _isPaid = true;
        _status = "PAID";
    }

    private void RecalculateTotal()
    {
        _totalAmount = _items.Sum(i => i.Price * i.Quantity);
    }
}

// Korrigiert: Prozessor verwendet Methoden von Order
public class FixedOrderProcessor
{
    private readonly IPaymentGateway _paymentGateway;

    public void ProcessPayment(FixedOrder order, PaymentDetails details)
    {
        // Methode von Order verwenden, um Zustand zu prüfen
        if (order.IsPaid)
            throw new InvalidOperationException("Bestellung bereits bezahlt");

        // Zahlung über Gateway verarbeiten
        var confirmation = _paymentGateway.Charge(details, order.TotalAmount);

        // Methode von Order verwenden, um Zustand zu aktualisieren
        order.MarkAsPaid(confirmation);
    }
}

CVE-Beispiele

Diese CWE ist für direkte CVE-Zuordnung als VERBOTEN markiert, da sie ein Codequalitäts-/Kapselungsproblem und keine direkte Sicherheitsschwachstelle darstellt.


Verwandte CWEs

  • CWE-1061: Insufficient Encapsulation (Eltern)
  • CWE-1227: Encapsulation Issues (Kategoriemitglied)
  • CWE-766: Critical Data Element Declared Public (verwandt)

Referenzen

  1. MITRE Corporation. "CWE-1090: Method Containing Access of a Member Element from Another Class." https://cwe.mitre.org/data/definitions/1090.html
  2. Martin, Robert C. "Clean Code" - Law of Demeter.
  3. Fowler, Martin. "TellDontAsk." https://martinfowler.com/bliki/TellDontAsk.html