Aufruf eines Kontrollelements auf einer unnötig tiefen horizontalen Schicht
Beschreibung
Aufruf eines Kontrollelements auf einer unnötig tiefen horizontalen Schicht tritt auf, wenn Code auf einer Architekturschicht Code aufruft, der sich auf einer tieferen Schicht als der benachbarten Schicht befindet und effektiv mindestens eine Zwischenschicht in der Hierarchie überspringt. Der aufgerufene Code ist nicht Teil einer vertikalen Utility-Schicht, die legitimerweise von jeder horizontalen Schicht aus zugänglich ist. Dies verletzt das Prinzip der Schichtenarchitektur, bei der jede Schicht nur mit ihren unmittelbaren Nachbarn kommunizieren sollte.
Risiko
Obwohl primär ein architektonisches Qualitätsproblem, hat unsachgemäßer Schichtaufruf indirekte Sicherheitsimplikationen. Wenn Schichten umgangen werden, können in Zwischenschichten implementierte Sicherheitsprüfungen umgangen werden. Die Architekturverletzung erschwert Sicherheitsaudits, da die tatsächlichen Aufrufpfade vom erwarteten Design abweichen. Die Wartung wird fehleranfällig, was möglicherweise zu inkonsistenter Implementierung von Sicherheitskontrollen führt. Änderungen an tiefen Schichten können unerwartet obere Schichten beeinflussen, die keine direkten Abhängigkeiten haben sollten.
Lösung
Erzwingen Sie eine strikte Schichtenarchitektur, bei der Komponenten nur in benachbarte Schichten aufrufen. Erstellen Sie explizite Schnittstellen an jeder Schichtgrenze. Verwenden Sie Dependency Injection, um die direkte Instanziierung von Tiefenschicht-Komponenten zu verhindern. Implementieren Sie Architektur-Fitness-Funktionen in CI/CD, um Schichtverletzungen zu erkennen. Wenn schichtübergreifender Zugriff legitimerweise erforderlich ist, erstellen Sie vertikale Utility-Services, die explizit für solchen Zugriff konzipiert sind. Dokumentieren Sie legitime Ausnahmen von den Schichtregeln. Verwenden Sie statische Analysetools, um Schichtgrenzenverletzungen zu erkennen.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Sonstiges | Bereich: Sonstiges Reduzierte Wartbarkeit - Schichtüberspringung macht die Codebasis schwerer zu verstehen, zu warten und sicher zu modifizieren. |
| Sonstiges | Bereich: Sonstiges Qualitätsverschlechterung - Das Umgehen von Zwischenschichten kann beabsichtigte Verarbeitungslogik einschließlich Sicherheitskontrollen überspringen. |
Beispielcode und Lösung
Verwundbarer Code
// Verwundbar: Controller-Schicht greift direkt auf Datenschicht zu, überspringt Service-Schicht
// Schicht 1: Controller (Präsentation)
@RestController
public class VerwundbarerBestellController {
// Verwundbar: Direkte Injektion des Repository (Datenschicht)
@Autowired
private BestellungRepository bestellungRepository; // Überspringt Service-Schicht!
@Autowired
private KundeRepository kundeRepository; // Überspringt Service-Schicht!
@GetMapping("/bestellungen/{id}")
public Bestellung getBestellung(@PathVariable Long id) {
// Verwundbar: Direkter Datenzugriff
// Umgeht:
// - Autorisierungsprüfungen, die in der Service-Schicht sein sollten
// - Geschäftsvalidierung
// - Audit-Protokollierung
// - Caching-Logik
return bestellungRepository.findById(id).orElseThrow();
}
@PostMapping("/bestellungen")
public Bestellung erstelleBestellung(@RequestBody BestellungAnfrage anfrage) {
// Verwundbar: Geschäftslogik im Controller
// Sollte in der Service-Schicht sein
Kunde kunde = kundeRepository.findById(anfrage.getKundeId())
.orElseThrow();
// Keine Service-Schicht-Validierung oder Autorisierung
Bestellung bestellung = new Bestellung();
bestellung.setKunde(kunde);
bestellung.setArtikel(anfrage.getArtikel());
bestellung.setGesamt(berechneGesamt(anfrage.getArtikel()));
return bestellungRepository.save(bestellung);
}
}
# Verwundbar: View-Schicht greift direkt auf Datenbank-Schicht zu
# Schicht 1: Views (Präsentation)
class VerwundbarerBestellView:
def get_bestellung(self, request, bestellung_id):
# Verwundbar: Direkter Datenbankzugriff aus View
# Überspringt Service/Geschäftsschicht
connection = database.get_connection() # Datenschicht
cursor = connection.cursor()
# Direktes SQL in der Präsentationsschicht!
cursor.execute(
"SELECT * FROM bestellungen WHERE id = %s",
(bestellung_id,)
)
bestellung = cursor.fetchone()
# Keine Geschäftsschicht-Validierung
# Keine Autorisierungsprüfung
return render_template("bestellung.html", bestellung=bestellung)
Sichere Lösung
// Sicher: Ordnungsgemäße Schichtenarchitektur, jede Schicht ruft nur benachbarte Schichten auf
// Schicht 1: Controller (Präsentationsschicht)
@RestController
public class SichererBestellController {
// Sicher: Hängt nur von Service-Schicht ab (benachbarte Schicht)
private final BestellungService bestellungService;
public SichererBestellController(BestellungService bestellungService) {
this.bestellungService = bestellungService;
}
@GetMapping("/bestellungen/{id}")
public ResponseEntity<BestellungDTO> getBestellung(
@PathVariable Long id,
@AuthenticationPrincipal User benutzer) {
// Sicher: Delegiert an Service-Schicht
// Service-Schicht handhabt Autorisierung, Validierung usw.
BestellungDTO bestellung = bestellungService.getBestellungById(id, benutzer);
return ResponseEntity.ok(bestellung);
}
@PostMapping("/bestellungen")
public ResponseEntity<BestellungDTO> erstelleBestellung(
@RequestBody @Valid BestellungAnfrage anfrage,
@AuthenticationPrincipal User benutzer) {
// Sicher: Alle Geschäftslogik in der Service-Schicht
BestellungDTO bestellung = bestellungService.erstelleBestellung(anfrage, benutzer);
return ResponseEntity.status(HttpStatus.CREATED).body(bestellung);
}
}
// Schicht 2: Service-Schicht (Geschäftslogik)
@Service
public class BestellungService {
// Sicher: Hängt nur von Repository-Schicht ab (benachbarte Schicht)
private final BestellungRepository bestellungRepository;
private final KundeRepository kundeRepository;
private final AutorisierungsService authService;
private final AuditService auditService;
@Transactional(readOnly = true)
public BestellungDTO getBestellungById(Long id, User benutzer) {
Bestellung bestellung = bestellungRepository.findById(id)
.orElseThrow(() -> new BestellungNichtGefundenException(id));
// Autorisierungsprüfung in der Service-Schicht
authService.prüfeBestellungszugriff(benutzer, bestellung);
// Audit-Protokollierung in der Service-Schicht
auditService.protokolliereBestellungszugriff(benutzer, bestellung);
return BestellungMapper.toDTO(bestellung);
}
@Transactional
public BestellungDTO erstelleBestellung(BestellungAnfrage anfrage, User benutzer) {
// Geschäftsvalidierung in der Service-Schicht
validiereBestellungsAnfrage(anfrage);
// Autorisierungsprüfung
authService.prüfeKannBestellungErstellen(benutzer);
// Geschäftslogik
Kunde kunde = kundeRepository.findById(anfrage.getKundeId())
.orElseThrow(() -> new KundeNichtGefundenException(anfrage.getKundeId()));
Bestellung bestellung = BestellungFactory.erstellen(kunde, anfrage.getArtikel());
Bestellung gespeichert = bestellungRepository.save(bestellung);
auditService.protokolliereBestellungserstellung(benutzer, gespeichert);
return BestellungMapper.toDTO(gespeichert);
}
}
// Schicht 3: Repository-Schicht (Datenzugriff)
@Repository
public interface BestellungRepository extends JpaRepository<Bestellung, Long> {
// Nur Datenzugriffsoperationen
// Geschäftslogik bleibt in der Service-Schicht
}
CVE-Beispiele
Diese CWE ist als VERBOTEN für direkte CVE-Zuordnung markiert, da sie eher ein architektonisches Qualitätsproblem als eine direkte Sicherheitsschwachstelle darstellt.
Referenzen
-
MITRE Corporation. "CWE-1054: Invocation of a Control Element at an Unnecessarily Deep Horizontal Layer." https://cwe.mitre.org/data/definitions/1054.html
-
CISQ. "Automated Source Code Quality Measures."
-
Fowler, Martin. "Patterns of Enterprise Application Architecture."