Unzureichende Kapselung
Beschreibung
Unzureichende Kapselung tritt auf, wenn ein Softwareprodukt es versäumt, interne Datenrepräsentation und Implementierungsdetails angemessen vor externen Komponenten zu verbergen. Dies ermöglicht externem Code den direkten Zugriff auf oder die Modifikation interner Daten, wodurch möglicherweise Validierung, Sicherheitskontrollen oder beabsichtigte Schnittstellen umgangen werden. Schlechte Kapselung verletzt das Prinzip des Information Hiding und erzeugt enge Kopplung zwischen Komponenten, was das System schwerer zu warten und zu sichern macht.
Risiko
Unzureichende Kapselung hat direkte Sicherheitsimplikationen. Exponierte interne Daten können von Angreifern modifiziert werden und Validierungs- oder Autorisierungsprüfungen umgehen. Sicherheitssensible Felder (Passwörter, Tokens, interner Zustand) können direkt zugänglich sein. Geschäftslogik kann umgangen werden, wenn interne Methoden öffentlich exponiert werden. Die enge Kopplung macht es schwierig, später Sicherheitskontrollen hinzuzufügen, ohne externe Abhängigkeiten zu brechen. Angreifer können exponierte Implementierungsdetails ausnutzen, um gezieltere Angriffe zu erstellen. Schlechte Kapselung erschwert auch Sicherheitsaudits, da die tatsächlichen Zugriffsmuster unklar sind.
Lösung
Wenden Sie ordnungsgemäße Zugriffsmodifikatoren (private, protected) an, um den Zugriff auf interne Daten einzuschränken. Verwenden Sie Getter/Setter-Methoden mit Validierung für kontrollierten Zugriff. Verbergen Sie Implementierungsdetails hinter Interfaces. Wenden Sie das Prinzip der minimalen Rechte auf Klassenmitglieder an. Machen Sie Klassen wo möglich unveränderlich. Verwenden Sie Kapselung, um Invarianten und Validierungsregeln durchzusetzen. Vermeiden Sie es, veränderbare interne Collections direkt zurückzugeben - geben Sie Kopien oder unveränderliche Ansichten zurück. Führen Sie Code-Reviews durch, die sich auf Zugriffskontrolle und Kapselung konzentrieren. Verwenden Sie statische Analysetools, um Kapselungsverstöße zu erkennen.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Zugriffskontrolle | Bereich: Zugriffskontrolle Schutzmechanismus umgehen - Angreifer können auf Daten oder Methoden zugreifen, die nicht öffentlich verfügbar sein sollten. |
| Integrität | Bereich: Integrität Anwendungsdaten modifizieren - Direkter Zugriff auf interne Daten umgeht Validierung und Sicherheitskontrollen. |
| Andere | Bereich: Ändere Reduzierte Wartbarkeit - Schlechte Kapselung erzeugt enge Kopplung, die Sicherheitsverbesserungen erschwert. |
Beispielcode
Anfälliger Code
// Anfällig: Öffentliche Member-Variablen, die sensible Daten exponieren
class VulnerableUser {
public:
// Anfällig: Alle Felder public - keine Kapselung
std::string username;
std::string password; // KRITISCH: Passwort direkt zugänglich!
std::string email;
std::string authToken; // KRITISCH: Token direkt zugänglich!
bool isAdmin; // Kann direkt modifiziert werden!
int failedLoginAttempts;
std::vector<std::string> permissions; // Veränderliche Collection exponiert
void login(const std::string& pwd) {
if (pwd == password) { // Direkter Vergleich - kein Hashing!
// Token generieren
}
}
};
// Angriffsszenario:
// VulnerableUser user;
// user.password = "bekannt"; // Authentifizierung umgehen
// user.isAdmin = true; // Rechteausweitung
// user.failedLoginAttempts = 0; // Sperrung zurücksetzen
// Anfällig: Veränderlichen internen Zustand exponieren
public class VulnerableAccount {
// Anfällig: Öffentliche Felder
public String accountNumber;
public double balance; // Kann direkt modifiziert werden!
public List<Transaction> transactions = new ArrayList<>();
// Anfällig: Gibt veränderliche interne Collection zurück
public List<Transaction> getTransactions() {
return transactions; // Externer Code kann modifizieren!
}
// Anfällig: Keine Validierung bei Abhebung
public void setBalance(double balance) {
this.balance = balance; // Keine Validierung - kann negativ setzen!
}
}
// Angriffsszenario:
// account.balance = 1000000; // Kontostand direkt setzen
// account.getTransactions().clear(); // Audit-Trail löschen
# Anfällig: Keine Kapselung in Python
class VulnerablePaymentProcessor:
def __init__(self):
# Anfällig: Alle Attribute direkt zugänglich
self.api_key = "sk_live_secret123" # Exponiert!
self.pending_charges = []
self.processed_amount = 0
self.config = {
'max_amount': 10000,
'retry_count': 3
}
def process_payment(self, amount):
if amount > self.config['max_amount']:
return False
self.processed_amount += amount
return True
# Angriffsszenario:
# processor = VulnerablePaymentProcessor()
# print(processor.api_key) # API-Schlüssel stehlen
# processor.config['max_amount'] = 999999999 # Limit umgehen
# processor.processed_amount = 0 # Audit-Summe zurücksetzen
Korrigierter Code
// Korrigiert: Ordnungsgemäße Kapselung mit privaten Membern
class FixedUser {
private:
std::string username_;
std::string passwordHash_; // Hash speichern, nicht Klartext-Passwort
std::string email_;
std::string authToken_;
bool isAdmin_;
int failedLoginAttempts_;
std::vector<std::string> permissions_;
public:
// Korrigiert: Konstruktor validiert und initialisiert
FixedUser(const std::string& username, const std::string& email)
: username_(username)
, email_(email)
, isAdmin_(false)
, failedLoginAttempts_(0) {
// Eingaben validieren
if (username.empty()) {
throw std::invalid_argument("Benutzername darf nicht leer sein");
}
}
// Korrigiert: Getter gibt Kopie sensibler Daten zurück
std::string getUsername() const { return username_; }
std::string getEmail() const { return email_; }
bool isAdmin() const { return isAdmin_; }
// Korrigiert: Kein direkter Setter für isAdmin - muss kontrollierte Methode verwenden
void promoteToAdmin(const FixedUser& promoter) {
if (!promoter.isAdmin()) {
throw std::runtime_error("Nur Admins können Benutzer befördern");
}
isAdmin_ = true;
}
// Korrigiert: Passwort wird gehasht, nie exponiert
void setPassword(const std::string& password) {
validatePasswordStrength(password);
passwordHash_ = hashPassword(password);
}
bool verifyPassword(const std::string& password) const {
return verifyHash(password, passwordHash_);
}
// Korrigiert: Unveränderliche Ansicht der Berechtigungen zurückgeben
std::vector<std::string> getPermissions() const {
return permissions_; // Gibt Kopie zurück
}
};
// Korrigiert: Unveränderliches Design mit ordnungsgemäßer Kapselung
public final class FixedAccount {
private final String accountNumber;
private double balance;
private final List<Transaction> transactions;
private final Object balanceLock = new Object();
public FixedAccount(String accountNumber, double initialBalance) {
if (accountNumber == null || accountNumber.isEmpty()) {
throw new IllegalArgumentException("Kontonummer erforderlich");
}
if (initialBalance < 0) {
throw new IllegalArgumentException("Anfangssaldo darf nicht negativ sein");
}
this.accountNumber = accountNumber;
this.balance = initialBalance;
this.transactions = new ArrayList<>();
}
// Korrigiert: Unveränderliches Feld
public String getAccountNumber() {
return accountNumber;
}
// Korrigiert: Thread-sicherer Saldozugriff
public double getBalance() {
synchronized (balanceLock) {
return balance;
}
}
// Korrigiert: Kontrollierte Saldomodifikation mit Validierung
public void deposit(double amount, String source) {
if (amount <= 0) {
throw new IllegalArgumentException("Einzahlungsbetrag muss positiv sein");
}
synchronized (balanceLock) {
balance += amount;
transactions.add(new Transaction("EINZAHLUNG", amount, source));
}
}
public boolean withdraw(double amount, String reason) {
if (amount <= 0) {
throw new IllegalArgumentException("Abhebungsbetrag muss positiv sein");
}
synchronized (balanceLock) {
if (balance < amount) {
return false;
}
balance -= amount;
transactions.add(new Transaction("ABHEBUNG", amount, reason));
return true;
}
}
// Korrigiert: Unveränderliche Ansicht der Transaktionen zurückgeben
public List<Transaction> getTransactions() {
return Collections.unmodifiableList(new ArrayList<>(transactions));
}
}
# Korrigiert: Python-Kapselung mit Properties und Validierung
class FixedPaymentProcessor:
def __init__(self, api_key: str):
# Korrigiert: Private Attribute (Konvention mit Unterstrich)
self._api_key = api_key
self._pending_charges: list = []
self._processed_amount: float = 0
self._config = {
'max_amount': 10000,
'retry_count': 3
}
self._config_frozen = False
# Korrigiert: Nur-Lese-Property - kein Setter exponiert
@property
def processed_amount(self) -> float:
return self._processed_amount
# Korrigiert: API-Schlüssel nie exponiert
# Kein Getter für _api_key überhaupt
# Korrigiert: Config als unveränderliche Kopie exponiert
@property
def config(self) -> dict:
return dict(self._config) # Kopie zurückgeben
def freeze_config(self):
"""Konfiguration nach Setup sperren"""
self._config_frozen = True
def update_config(self, key: str, value, admin_token: str):
"""Kontrollierte Config-Aktualisierung mit Autorisierung"""
if self._config_frozen:
raise RuntimeError("Konfiguration ist gesperrt")
if not self._verify_admin(admin_token):
raise PermissionError("Admin-Zugriff erforderlich")
if key not in self._config:
raise KeyError(f"Unbekannter Config-Schlüssel: {key}")
self._config[key] = value
def process_payment(self, amount: float) -> bool:
if amount <= 0:
raise ValueError("Betrag muss positiv sein")
if amount > self._config['max_amount']:
return False
# _api_key intern verwenden ohne es zu exponieren
result = self._call_payment_api(amount)
if result:
self._processed_amount += amount
return result
def _call_payment_api(self, amount: float) -> bool:
# Interne Methode verwendet _api_key
# API-Schlüssel verlässt diese Klasse nie
return True # Vereinfacht
CVE-Beispiele
- CVE-2010-3860: Exponierte interne Konfiguration ermöglichte unbefugten Zugriff.
- CVE-2019-0232: Unzureichende Kapselung im CGI-Servlet führte zu Command Injection.
Verwandte CWEs
- CWE-710: Improper Adherence to Coding Standards (Eltern)
- CWE-766: Critical Data Element Declared Public (Kind)
- CWE-1227: Encapsulation Issues (Kategoriemitglied)
Referenzen
-
MITRE Corporation. "CWE-1061: Insufficient Encapsulation." https://cwe.mitre.org/data/definitions/1061.html
-
Oracle. "Java Tutorials - Controlling Access to Members of a Class."
-
Martin, Robert C. "Clean Code: A Handbook of Agile Software Craftsmanship."