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
| Auswirkung | Details |
|---|---|
| Andere | Bereich: Ändere Reduzierte Wartbarkeit - Enge Kopplung macht Code schwerer sicher zu ändern. |
| Andere | Bereich: Ändere Erhöhte analytische Komplexität - Sicherheitsanalyse ist mit unklaren Grenzen schwieriger. |
| Integrität | Bereich: 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
- MITRE Corporation. "CWE-1090: Method Containing Access of a Member Element from Another Class." https://cwe.mitre.org/data/definitions/1090.html
- Martin, Robert C. "Clean Code" - Law of Demeter.
- Fowler, Martin. "TellDontAsk." https://martinfowler.com/bliki/TellDontAsk.html