Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

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_cost und reward_type werden bereits über die Attribut-Definition selbst (required => true) sowie das Source Model RewardType (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\Index validiert die Prämien-ID gegen die aktive Reward-Collection, bevor RewardRedemptionManagementInterface::redeem() überhaupt aufgerufen wird - ein doppelter Schutz, denn die Repository-Implementierung selbst prüft is_active erneut (Kapitel 81).
  • Webapi-Routen (Kapitel 80/81): force="true" auf dem customerId-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): PointsSummary prüft $context->getExtensionAttributes()->getIsCustomer() statt einer reinen getUserId() > 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:

  1. Eingeloggte Storefront-Kunden (Redeem\Index, Kapitel 50): implementiert CsrfAwareActionInterface, beide Methoden geben null zurück - damit greift Magentos Standard-Formkey-Validierung unverändert, ohne eigenen Code.
  2. Gastfähige AJAX-Aufrufe im Checkout (Ajax\ApplyPoints, Kapitel 63): bewusst ohne AccountInterface, 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.
  3. 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.