Sicherheitsüberlegungen: ACL, Input-Validierung, CSRF über alle Bereiche
Sicherheitsüberlegungen: ACL, Input-Validierung, CSRF über alle Bereiche
~9 Min. Lesezeit Zuletzt aktualisiert am 9. August 2026
Sicherheit war in dieser Serie nie ein eigener Block - sie steckte in jedem einzelnen Controller, jeder API-Route und jeder Zahlungsart als Teil des jeweiligen Kapitels. Dieses Kapitel zieht die verstreuten Entscheidungen zu einem zusammenhängenden Bild zusammen: vier ACL-Ressourcen, drei unterschiedliche CSRF-Strategien je nach Kontext, und eine durchgängige Regel gegen SQL-Injection über EAV- und Flat-Collections hinweg.
Die vier ACL-Ressourcen im Baum
Die vier ACL-Ressourcen dieses Moduls
Magento_Backend::admin
`-- Mironsoft_Loyalty::loyalty (Kapitel 6/9, Top-Level-Ressource)
|-- Mironsoft_Loyalty::rewards (Kapitel 16, Prämien-Admin-Grid)
`-- Mironsoft_Loyalty::points_view (Kapitel 80, Staff-Zugriff auf fremde Punktestände)
Magento_Config::config
`-- Mironsoft_Loyalty::config_section (Kapitel 6/7, eigene Konfigurationsseite)Bemerkenswert ist die bewusste Trennung zwischen Mironsoft_Loyalty::points_view (Kapitel 80) und der REST-Route GET /V1/loyalty/points/mine, die stattdessen resource ref="self" nutzt: Ein eingeloggter Kunde braucht keine explizite ACL-Berechtigung, um den eigenen Punktestand abzufragen - self ist Magentos eingebauter Mechanismus für "jeder authentifizierte Kunde darf das für sich selbst". points_view gilt ausschließlich für die zweite, für Mitarbeiter gedachte Route GET /V1/loyalty/points/customer/:customerId, die beliebige Kunden-IDs akzeptiert.
Input-Validierung je Eintrittspunkt
- EAV-Attribute (Kapitel 17): Pflichtfelder wie
title,points_costundreward_typewerden bereits über die Attribut-Definition selbst (required => true) sowie das Source ModelRewardType(Kapitel 14) auf gültige Werte begrenzt - Validierung, die nie den PHP-Code der Repository-Implementierung erreicht, weil sie schon beim Speichern des Attributs greift. - Storefront-Controller (Kapitel 50):
Redeem\Indexvalidiert die Prämien-ID gegen die aktive Reward-Collection, bevorRewardRedemptionManagementInterface::redeem()überhaupt aufgerufen wird - ein doppelter Schutz, denn die Repository-Implementierung selbst prüftis_activeerneut (Kapitel 81). - Webapi-Routen (Kapitel 80/81):
force="true"auf demcustomerId-Parameter der/mine-Route überschreibt jeden vom Client mitgeschickten Wert mit der ID aus dem Auth-Token - der klassische Schutz gegen IDOR (Insecure Direct Object Reference), ohne den ein Kunde durch einfaches Ändern der Request-Parameter fremde Punktestände abfragen könnte. - GraphQL-Resolver (Kapitel 82/83):
PointsSummaryprüft$context->getExtensionAttributes()->getIsCustomer()statt einer reinengetUserId() > 0-Prüfung - genau die Unterscheidung, die auch die GraphQL-Serie dieses Katalogs an zentraler Stelle einschärft.
Drei CSRF-Strategien - je nach Kontext
Kein einziger POST-Endpunkt dieses Moduls verzichtet auf einen CSRF-Schutz - aber die konkrete Umsetzung unterscheidet sich bewusst nach Zielgruppe:
- Eingeloggte Storefront-Kunden (
Redeem\Index, Kapitel 50): implementiertCsrfAwareActionInterface, beide Methoden gebennullzurück - damit greift Magentos Standard-Formkey-Validierung unverändert, ohne eigenen Code. - Gastfähige AJAX-Aufrufe im Checkout (
Ajax\ApplyPoints, Kapitel 63): bewusst ohneAccountInterface, weil ein Login-Redirect den AJAX-Aufruf zerstören würde - stattdessen eine manuelle 401-JSON-Antwort für nicht eingeloggte Nutzer statt eines Redirects, während der reguläre Formkey-Schutz weiterhin greift. - REST und GraphQL (Block 10): kein Formkey-Konzept nötig - REST läuft über Token-Authentifizierung, GraphQL-Mutations sind zustandslos gegenüber Cookies; stattdessen übernimmt
ThrottleRewardRedemptionPlugin(Kapitel 86) die Missbrauchsabsicherung auf Geschäftslogik-Ebene.
Rate-Limiting als Sicherheitsmaßnahme
RedemptionRateLimiter (Kapitel 86, MAX_ATTEMPTS = 5, WINDOW_SECONDS = 60) schützt RewardRedemptionManagementInterface::redeem() mit einem einzigen Plugin gegen automatisierte Einlöseversuche über beide Transportwege gleichzeitig - REST und GraphQL rufen dieselbe Methode auf, ein einziger Check reicht. Die bewusst offene Lücke: die gästefähige loyaltyRewards-Query hat keinen Kunden-Kontext für einen Cache-Schlüssel und bleibt auf Infrastruktur-Ebene angewiesen (Nginx limit_req, Kapitel 86).
SQL-Injection: eine durchgängige Regel
addFieldToFilter() mit einem Integer-Wert wird in diesem Modul ausnahmslos als ['eq' => $value] geschrieben - nie als nackter Skalar (CLAUDE.md, seit Kapitel 4 durchgehend eingehalten). Dieselbe Disziplin gilt für die EAV-Collection: addAttributeToFilter() mit Array-Operatoren statt Zeichenketten-Konkatenation, konsequent von Kapitel 15 bis zum GraphQL-DataProvider in Kapitel 82.
Achtung: Keine dieser Maßnahmen schließt die dokumentierten, bewusst offen gelassenen Race Conditions bei parallelen Einlösungen (Kapitel 63/81/86, fehlendes SELECT ... FOR UPDATE). Sie sind ein Sicherheitsrisiko anderer Art - kein Injection- oder Autorisierungsproblem, sondern ein Nebenläufigkeitsproblem, das in einem echten Produktivmodul vor dem Go-Live geschlossen werden sollte.
Sichere Bausteine allein ergeben noch kein erweiterbares Modul - Kapitel 102 zeigt, wie andere Entwickler auf genau dieser Architektur aufbauen können, ohne eine einzige Zeile in Mironsoft_Loyalty selbst zu ändern.