Verletzung sicherer Designprinzipien
Beschreibung
Verletzung sicherer Designprinzipien tritt auf, wenn Softwarearchitektur und -design etablierte Sicherheitsprinzipien nicht befolgen. Dies umfasst die Verletzung des Prinzips der geringsten Privilegien, Defense in Depth, sichere Standardeinstellungen, Einfachheit der Mechanismen, vollständige Vermittlung, offenes Design, Privilegientrennung, geringstes gemeinsames Mechanismus-Prinzip und psychologische Akzeptanz. Schlechtes Design führt zu systemischen Schwachstellen, die später schwer zu beheben sind.
Risiko
Architekturelle Schwachstellen durchdringen ganze Systeme. Einzelne Fehlerpunkte kompromittieren die gesamte Sicherheit. Übermäßige Privilegien ermöglichen größere Sicherheitsverletzungen. Fehlende Verteidigungsschichten ermöglichen einfache Ausnutzung. Komplexe Designs verbergen Sicherheitslücken. Schlechte Kompartimentierung ermöglicht laterale Bewegung. Design-Fehler erfordern kostspielige Neuentwicklungen zur Behebung.
Lösung
Wenden Sie Sicherheitsprinzipien bereits in der Designphase an. Implementieren Sie Defense in Depth. Befolgen Sie das Prinzip der geringsten Privilegien. Verwenden Sie sichere Standardeinstellungen. Halten Sie Sicherheitsmechanismen einfach. Überprüfen Sie die Autorisierung bei jedem Zugriff. Trennen Sie kritische Funktionen. Minimieren Sie gemeinsam genutzte Ressourcen. Entwerfen Sie für Benutzbarkeit und Sicherheit gemeinsam.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Sicherheit | Bereich: Systemische Schwachstellen Designfehler betreffen die gesamte Anwendung. |
| Wartung | Bereich: Technische Schulden Unsicheres Design erfordert umfangreiches Refactoring. |
| Compliance | Bereich: Audit-Fehler Verstöße gegen Sicherheitsstandards und Vorschriften. |
Beispielcode und Lösung
Verwundbarer Code
// VERLETZUNG: Geringste Privilegien - Alles läuft als Admin
public class VulnerableService {
// VERLETZUNG: Dienst verwendet Admin-Anmeldedaten für alles
private DataSource adminConnection = createAdminConnection();
public List<Product> getPublicProducts() {
// Verwendet Admin-Verbindung für schreibgeschützte öffentliche Abfrage
return adminConnection.query("SELECT * FROM products WHERE public = true");
}
public void deleteUser(String userId) {
// Gleiche Admin-Verbindung
adminConnection.execute("DELETE FROM users WHERE id = ?", userId);
}
}
// VERLETZUNG: Defense in Depth - Einzelne Sicherheitsschicht
public class VulnerableSingleLayerSecurity {
public void accessResource(String userId, String resourceId) {
// VERLETZUNG: Nur eine Sicherheitsprüfung
if (isAuthenticated(userId)) {
// Keine Autorisierungsprüfung
// Keine Eingabevalidierung
// Keine Audit-Protokollierung
return getResource(resourceId);
}
}
}
// VERLETZUNG: Sichere Standardeinstellungen - Standard ist Erlauben
public class VulnerableDefaultAllow {
public boolean checkPermission(String userId, String action) {
try {
Permission perm = permissionService.get(userId, action);
return perm.isAllowed();
} catch (Exception e) {
// VERLETZUNG: Standard ist Erlauben bei Fehler
return true;
}
}
}
// VERLETZUNG: Einfachheit der Mechanismen - Überkomplizierte Sicherheit
public class VulnerableComplexSecurity {
public boolean authenticate(Request req) {
// VERLETZUNG: Komplex, schwer zu prüfen
return checkHeader(req) &&
checkCookie(req) &&
checkSession(req) &&
checkToken(req) &&
checkCertificate(req) &&
checkIPRange(req) &&
checkTimeOfDay(req) &&
checkMoonPhase(req) && // Scherz, aber illustriert Komplexität
validateAllTheThings(req);
}
}
# VERLETZUNG: Vollständige Vermittlung - Nicht jeden Zugriff prüfen
class VulnerableIncompleteMediation:
def __init__(self):
self.verified_users = set()
def access_resource(self, user_id, resource_id):
# VERLETZUNG: Prüft nur beim ersten Mal
if user_id not in self.verified_users:
if self.check_authorization(user_id, resource_id):
self.verified_users.add(user_id) # Für immer gecacht
else:
raise PermissionError()
# Nachfolgende Zugriffe überspringen die Autorisierung
return self.get_resource(resource_id)
# VERLETZUNG: Privilegientrennung - Ein Schlüssel für alles
class VulnerableSinglePrivilege:
def __init__(self, master_key):
# VERLETZUNG: Ein Schlüssel kontrolliert alles
self.master_key = master_key
def authenticate(self, key):
# Master-Schlüssel gewährt vollen Zugriff
return key == self.master_key
def delete_data(self, key):
if self.authenticate(key):
self.db.delete_all()
def read_secrets(self, key):
if self.authenticate(key):
return self.get_all_secrets()
# VERLETZUNG: Geringstes gemeinsames Mechanismus-Prinzip - Gemeinsame Ressourcen
class VulnerableSharedResources:
# VERLETZUNG: Alle Mandanten teilen sich denselben Verbindungspool
shared_pool = ConnectionPool()
# VERLETZUNG: Alle Mandanten teilen sich denselben Cache
shared_cache = Cache()
# VERLETZUNG: Alle Mandanten teilen sich denselben Dateispeicher
shared_storage = FileStorage('/data')
def get_tenant_data(self, tenant_id, query):
# Verlasst sich auf Abfrage zur Datenisolierung - nicht auf Ressourcenebene erzwungen
return self.shared_pool.execute(
f"SELECT * FROM data WHERE tenant_id = '{tenant_id}'"
)
// VERLETZUNG: Mehrere Prinzipien in Webanwendung
class VulnerableWebApp {
constructor() {
// VERLETZUNG: Geringste Privilegien - Globaler Admin-Kontext
this.db = new DatabaseConnection({ user: 'admin' });
}
// VERLETZUNG: Defense in Depth - Einzelne Auth-Prüfung
async handleRequest(req) {
if (req.cookies.session) {
// Keine Token-Validierung
// Kein CSRF-Schutz
// Kein Rate-Limiting
return this.processRequest(req);
}
return { error: 'Unauthorized' };
}
// VERLETZUNG: Sichere Standardeinstellungen
async checkAccess(userId, resource) {
try {
const allowed = await this.permissionService.check(userId, resource);
return allowed;
} catch (error) {
// VERLETZUNG: Fehler = erlauben
console.error(error);
return true;
}
}
// VERLETZUNG: Vollständige Vermittlung - Auth-Entscheidungen cachen
async getResource(userId, resourceId) {
const cacheKey = `auth:${userId}`;
// VERLETZUNG: Gecachte Auth-Entscheidung ohne Aktualisierung verwendet
if (this.cache.has(cacheKey)) {
return this.loadResource(resourceId);
}
if (await this.checkAccess(userId, resourceId)) {
// Für immer cachen - Berechtigungsänderungen nicht reflektiert
this.cache.set(cacheKey, true);
return this.loadResource(resourceId);
}
throw new Error('Forbidden');
}
}
// VERLETZUNG: Offenes Design - Sicherheit hängt von Geheimhaltung ab
const SECRET_ADMIN_PATH = '/xK9mN2pL/admin'; // "Versteckter" Admin
app.get(SECRET_ADMIN_PATH, (req, res) => {
// Keine Authentifizierung - URL-Geheimhaltung ist die "Sicherheit"
res.json(getAllAdminData());
});
Sichere Lösung
// KORREKT: Befolgt sichere Designprinzipien
public class SecureService {
// PRINZIP: Geringste Privilegien - Separate Verbindungen pro Rolle
private DataSource readOnlyConnection;
private DataSource writeConnection;
private DataSource adminConnection;
// PRINZIP: Einfachheit der Mechanismen - Einfach, prüfbar
public List<Product> getPublicProducts() {
// Verwendet minimale Privilegien für die Operation
return readOnlyConnection.query(
"SELECT * FROM products WHERE public = true"
);
}
@RequiresRole("ADMIN")
@AuditLogged
public void deleteUser(String adminUserId, String targetUserId) {
// Verwendet Admin-Verbindung nur für Admin-Operationen
adminConnection.execute("DELETE FROM users WHERE id = ?", targetUserId);
}
}
// KORREKT: Defense in Depth - Mehrere Sicherheitsschichten
public class SecureMultiLayerSecurity {
public Object accessResource(Request request, String resourceId) {
// Schicht 1: Eingabevalidierung
if (!validator.isValidResourceId(resourceId)) {
throw new InvalidInputException();
}
// Schicht 2: Authentifizierung
User user = authService.authenticate(request);
if (user == null) {
throw new AuthenticationException();
}
// Schicht 3: Autorisierung
if (!authzService.canAccess(user, resourceId)) {
throw new AuthorizationException();
}
// Schicht 4: Rate-Limiting
if (!rateLimiter.allowRequest(user.getId())) {
throw new RateLimitException();
}
// Schicht 5: Ressource abrufen
Object resource = resourceService.get(resourceId);
// Schicht 6: Audit-Protokollierung
auditLog.logAccess(user, resourceId);
return resource;
}
}
// KORREKT: Sichere Standardeinstellungen - Standard ist Verweigern
public class SecureDefaultDeny {
public boolean checkPermission(String userId, String action) {
// Standard ist Verweigern
boolean allowed = false;
try {
Permission perm = permissionService.get(userId, action);
if (perm != null) {
allowed = perm.isAllowed();
}
} catch (Exception e) {
// PRINZIP: Fail-Safe - bei Fehler verweigern
log.error("Permission check failed, denying access", e);
allowed = false;
}
return allowed;
}
}
// KORREKT: Privilegientrennung - Mehrere Faktoren erforderlich
public class SecureSeparatedPrivilege {
public boolean authorizeHighRiskAction(
String userId,
String password,
String totpCode,
String approverUserId
) {
// PRINZIP: Mehrere unabhängige Faktoren
// Faktor 1: Benutzer authentifiziert
if (!authService.verifyPassword(userId, password)) {
return false;
}
// Faktor 2: MFA verifiziert
if (!totpService.verify(userId, totpCode)) {
return false;
}
// Faktor 3: Genehmigung durch zweite Person (Funktionstrennung)
if (!approvalService.isApproved(approverUserId, userId)) {
return false;
}
return true;
}
}
# KORREKT: Vollständige Vermittlung - Jeden Zugriff prüfen
class SecureCompleteMediation:
def access_resource(self, user_id, resource_id):
# PRINZIP: Immer bei jeder Anfrage verifizieren
# Niemals auf gecachte Autorisierungsentscheidungen verlassen
# Authentifizierung verifizieren
user = self.auth_service.verify_session(user_id)
if not user:
raise AuthenticationError()
# Autorisierung verifizieren (frische Prüfung, nicht gecacht)
if not self.authz_service.can_access(user, resource_id):
raise AuthorizationError()
# Ressource abrufen
resource = self.resource_service.get(resource_id)
# Audit
self.audit_log.log_access(user, resource_id)
return resource
# KORREKT: Geringstes gemeinsames Mechanismus-Prinzip - Isolierte Ressourcen pro Mandant
class SecureIsolatedResources:
def __init__(self):
self.tenant_pools = {}
self.tenant_caches = {}
self.tenant_storage = {}
def get_tenant_pool(self, tenant_id):
# PRINZIP: Jeder Mandant bekommt isolierte Ressourcen
if tenant_id not in self.tenant_pools:
self.tenant_pools[tenant_id] = ConnectionPool(
database=f'tenant_{tenant_id}_db',
user=f'tenant_{tenant_id}_user'
)
return self.tenant_pools[tenant_id]
def get_tenant_data(self, tenant_id, query):
# Verwendet mandantenspezifische Verbindung
# Isolation auf Datenbankebene, nicht nur Abfragefilterung
pool = self.get_tenant_pool(tenant_id)
return pool.execute(query)
# KORREKT: Defense-in-Depth-Implementierung
class SecureDefenseInDepth:
def process_request(self, request):
# Schicht 1: TLS/Transportsicherheit (auf Infrastrukturebene gehandhabt)
# Schicht 2: Eingabevalidierung
validated_input = self.validate_input(request)
# Schicht 3: Authentifizierung
user = self.authenticate(request)
# Schicht 4: Autorisierung
self.authorize(user, validated_input.action)
# Schicht 5: Geschäftslogik mit Validierung
result = self.execute_action(user, validated_input)
# Schicht 6: Ausgabekodierung
safe_output = self.encode_output(result)
# Schicht 7: Audit-Protokollierung
self.audit_log(user, validated_input.action, result.success)
return safe_output
// KORREKT: Befolgt alle sicheren Designprinzipien
class SecureWebApp {
constructor() {
// PRINZIP: Geringste Privilegien - Verschiedene Verbindungen für verschiedene Bedürfnisse
this.readOnlyDb = new DatabaseConnection({ user: 'reader', readOnly: true });
this.writeDb = new DatabaseConnection({ user: 'writer' });
this.adminDb = new DatabaseConnection({ user: 'admin' });
}
// PRINZIP: Defense in Depth - Mehrere Sicherheitsschichten
async handleRequest(req) {
// Schicht 1: Eingabevalidierung
const validatedInput = this.validateInput(req.body);
// Schicht 2: Rate-Limiting
if (!await this.rateLimiter.check(req.ip)) {
throw new RateLimitError();
}
// Schicht 3: Authentifizierung
const user = await this.authenticate(req);
// Schicht 4: CSRF-Validierung
if (!this.validateCsrfToken(req, user)) {
throw new CsrfError();
}
// Schicht 5: Autorisierung
if (!await this.authorize(user, validatedInput.action)) {
throw new AuthorizationError();
}
// Schicht 6: Anfrage verarbeiten
const result = await this.processRequest(user, validatedInput);
// Schicht 7: Audit-Protokollierung
await this.auditLog(user, validatedInput.action, result);
return result;
}
// PRINZIP: Sichere Standardeinstellungen - Bei Fehler verweigern
async checkAccess(userId, resource) {
try {
const allowed = await this.permissionService.check(userId, resource);
return allowed === true; // Explizite true-Prüfung
} catch (error) {
// Protokollieren und verweigern
this.logger.error('Access check failed', { userId, resource, error });
return false; // Fail-Safe: verweigern
}
}
// PRINZIP: Vollständige Vermittlung - Frische Autorisierungsprüfung jedes Mal
async getResource(userId, resourceId) {
// Immer Autorisierung verifizieren (kein Caching von Auth-Entscheidungen)
if (!await this.authorize(userId, resourceId)) {
throw new ForbiddenError();
}
const resource = await this.loadResource(resourceId);
// Zugriff protokollieren
await this.auditLog({ userId, action: 'read', resourceId });
return resource;
}
// PRINZIP: Privilegientrennung für risikoreiche Operationen
async deleteAllUserData(userId, password, totpCode, adminApproval) {
// Mehrere Faktoren erforderlich
const passwordValid = await this.verifyPassword(userId, password);
const totpValid = await this.verifyTotp(userId, totpCode);
const approved = await this.verifyAdminApproval(adminApproval);
if (!passwordValid || !totpValid || !approved) {
throw new InsufficientPrivilegeError();
}
return this.performDeletion(userId);
}
}
// PRINZIP: Offenes Design - Sicherheit hängt nicht von Geheimhaltung ab
// Admin-Routen sind dokumentiert, erfordern aber Authentifizierung
app.get('/api/admin/users',
authenticate,
requireRole('admin'),
auditLog('admin_users_list'),
adminController.listUsers
);
Ausgenutzt in der Praxis
Privilegien-Eskalation
Fehlendes Prinzip der geringsten Privilegien führte zu massiven Sicherheitsverletzungen.
Einzelne Fehlerpunkte
Fehlendes Defense in Depth ermöglichte vollständige Kompromittierung.
Gecachte Auth-Umgehung
Autorisierungs-Caching ermöglichte Zugriff nach Widerruf.
Tools zum Testen und Ausnutzen
-
Architektur-Review-Tools.
-
Bedrohungsmodellierungs-Frameworks.
-
Sicherheitsdesign-Checklisten.
CVE-Beispiele
-
Architekturelle Fehler in großen Anwendungen.
-
Schwachstellen auf Designebene.
Referenzen
-
MITRE. "CWE-657: Violation of Secure Design Principles." https://cwe.mitre.org/data/definitions/657.html
-
Saltzer und Schröder. "The Protection of Information in Computer Systems."