Skip to content

Audit-Log ​

Jede systemrelevante Aktion im CMS landet in einem manipulationssicheren Audit-Log: Backend-Logins und 2FA-Events, Content-Aenderungen (Pagebuilder, Menue-Editor, Media), Konfigurationsaenderungen (API-Keys, AI-Provider, Shopware 6, Elasticsearch), Daten-Exporte, Deployments, Plugin-Installation/Deinstallation. Jeder Eintrag traegt den ausloesenden User, die echte Client-IP, den Browser, einen Vorher/Nachher-Snapshot und einen SHA256-HMAC-Hash, der ihn kryptographisch an den Vorgaenger-Eintrag bindet. Ein taeglicher Verifier prueft, dass die Kette nicht veraendert wurde.

Oeffne Komponenten → Log.

Audit-Log-Tabelle mit Filterleiste, Chain-Status-Badge und Severity-Spalte

Was geloggt wird ​

Audit-Eintraege entstehen ueber zwei Pfade:

  • Webhook-Hook — jedes interne Webhook-Event (auth.login, media.deleted, item.{table}.created/updated/deleted, deployment.completed etc.) wird automatisch mitgeloggt.
  • Direkter Hook — explizite AuditLogger::record()-Aufrufe fuer Events ohne Webhook (Failed-Logins, 2FA-Setup, Password-Resets, API-Key-Mutationen, CSV-Exporte, sensitive Reads).

Insgesamt etwa 75 unterschiedliche Event-Typen, gruppiert nach Domain-Praefix (auth.*, config.*, data.*, pagebuilder.*, menu.*, media.*, system.*, security.*, audit.*, emailmarketing.*, formfunnel.*, search.*, plus item.{table}.*-Wildcard fuer jede Plugin-Tabelle).

Schweregrade ​

Jeder Eintrag hat einen von drei Schweregraden. Die Header-Filter und die Zeilen-Badges nutzen das gleiche Farbschema.

LevelWann es feuertBeispiele
Info (cyan)Normale, erwartete Aktivitaet. Audit-Wert, aber kein Alarm.Erfolgreicher Login, Seite veroeffentlicht, Item erstellt/aktualisiert, sensitive Reads (Rechnung/Zertifikat/Bestelldetail), Audit-Log-Zugriff, manueller Cron-Trigger.
Warning (orange)Sicherheitsrelevant oder destruktiv. Bedarf Aufmerksamkeit.Failed Login, 2FA-Verify fehlgeschlagen, Account gesperrt, Session-IP-Wechsel, Rate-Limit ueberschritten, alle *.deleted-Events, CSV-Export, AI-Massengenerierung, Cache geleert.
Critical (rot)System-Level-Aenderungen. Pruefer sollte hier immer hinschauen.Permission geaendert, Passwort zurueckgesetzt, 2FA aktiviert, API-Key erstellt/aktualisiert/geloescht, AI-/Email-/Shopware-/Elasticsearch-Verbindung mutiert, Plugin installiert/deinstalliert, Migrationen ausgefuehrt, Deployment-Ziel angelegt/geaendert.

Auditor-Workflow

Erst auf Critical filtern — dort liegen alle privilegierten Operationen in einer Ansicht. Dann Warning fuer Security-Events und Loeschungen. Info ist fuer forensische Korrelation, wenn du den vollstaendigen Aktivitaetspfad eines Users oder einer Resource brauchst.

Spalten ​

SpalteBedeutung
ZeitstempelWann das Ereignis passiert ist (Server-Zeitzone).
EventHierarchischer Event-Bezeichner (domain.action.state) mit einem Icon fuer die Domain-Gruppe.
UserBackend-Benutzername, oder — bei anonymen Events (Failed Login, oeffentliches Form-Submit).
ResourceDie betroffene Entitaet, z.B. user #42, page #17, apikey #5.
IPEchte Client-IP — Cloudflare-aware. Hinter einem Reverse-Proxy steht die echte Client-IP, nicht die Edge-IP.
SeverityInfo / Warning / Critical-Badge.
AktionenOeffnet das Details-Modal mit dem vollstaendigen Eintrag.

1. Log filtern ​

Die Filterleiste im Header schraenkt die Tabelle ein. Alle Filter kombinieren sich (UND-Verknuepfung).

FilterVerhalten
Event typeMulti-Group-Dropdown nach Authentication / Configuration / Data / Content. auth. matcht jedes Event mit auth.-Praefix; auth.login nur exakt das Event.
Resource typeFreitext-Praefix-Match auf resource_type — z.B. user, page, media, apikey.
User IDNumerisch — die ID eines Backend-Users. ID findest du unter Komponenten → Benutzer.
SeverityAlle / Info / Warning / Critical.
From / ToDatumszeit-Range. Hilfreich bei Incident-Untersuchung um einen bestimmten Zeitslot.
SucheFreitext gegen Event-Typ, Resource-ID, IP, Statement und Username.

Klick auf Reset loescht alle Filter.

2. Eintrag oeffnen ​

Klick auf Details in einer Zeile. Das Modal oeffnet mit allen Feldern, dem Vorher/Nachher-Diff und dem Chain-Kontext.

Audit-Log-Detail-Modal mit Vorher/Nachher-JSON-Diff und Hash-Chain-Kontext

Die Detail-Ansicht zeigt:

  • Zeitstempel, User, IP, User-Agent, Resource, Severity — die Metadaten des Eintrags.
  • Vorher / Nachher — JSON-Snapshots nebeneinander. Sensitive Felder (password, secret, key, token, salt) werden automatisch herausgefiltert. JSONs groesser 64 KB werden mit einem [truncated]-Marker abgeschnitten — der Truncation-Marker selbst gehoert zum Hash.
  • Hash-Chain — der hash des Eintrags (HMAC-SHA256 ueber Event-Typ, User, Resource, IP, Vorher/Nachher, Zeitstempel) plus der prev_hash, an den er gebunden ist.
  • Chain-Kontext — die zwei Vorgaenger und zwei Nachfolger. So siehst du den Eintrag in seiner zeitlichen Nachbarschaft.

Self-Log

Das Oeffnen des Detail-Modals schreibt selbst einen audit.read-Eintrag — der Audit-Log protokolliert, wer welche Eintraege angesehen hat. Das ist beabsichtigt und Teil des SOC2-Access-Trackings.

3. Chain verifizieren ​

Der Header traegt ein Chain-Status-Badge:

BadgeBedeutung
🟢 Chain OKDer Verifier lief, jeder Eintrags-Hash stimmt mit dem neu berechneten HMAC ueberein.
🟡 Never verifiedNoch kein Verifier-Run — Initialzustand bei frischer Installation.
🔴 Chain broken (pulsierend)Ein Verify-Run hat einen Mismatch gefunden. Hover ueber das Badge zeigt die ID und den Grund.

Klick auf Verify chain triggert einen On-Demand-Verifier-Lauf. Der Lauf ist asynchron: die Antwort kommt sofort zurueck, das Badge aktualisiert sich innerhalb von ~30 Sekunden ueber Polling.

Chain-Status-Badge und Verify-Button im Audit-Log-Header

Ein Scheduled Task (verify_audit_chain.php) laeuft automatisch alle 24 Stunden.

Chain broken — was tun?

Ein rotes Badge bedeutet, dass der Audit-Log ausserhalb der Anwendung manipuliert wurde. Im error_log steht eine Zeile wie [AuditChainVerifier] CHAIN BROKEN at id=X (last_verified=Y): hash mismatch on id X: expected …, got …. Untersuche, bevor du etwas anfasst: ein Angreifer mit DB-Zugriff ist der Worst Case; der zweitschlimmste Fall ist ein versehentliches ALTER TABLE / Daten-Import, das Zeilen mutiert hat.

Aufbewahrung ​

StufeDauerTabelle
HotErste 365 Tagelog (volle Indizes — schnelle Filter + Sortierung)
Cold-ArchivJahre 2–7log_archive (schlankere Indizes — langsamere Lookups, weniger Speicher)
Hard-CleanupNach 7 JahrenEndgueltig geloescht

Drei Scheduled Tasks halten den Lifecycle:

  • verify_audit_chain.php — taeglich, prueft die Chain.
  • archive_audit_log.php — taeglich, verschiebt Eintraege aelter als 365 Tage in log_archive (1000 Zeilen pro Batch).
  • cleanup_audit_archive.php — monatlich, loescht Archiv-Zeilen aelter als 7 Jahre.

DSGVO ​

IP und User-Agent sind nach DSGVO personenbezogene Daten (EuGH C-582/14, Breyer). Sie in einem Audit-Trail zu loggen ist nach Art. 6 (1) f DSGVO zulaessig (berechtigtes Interesse an IT-Sicherheit, Betrugspraevention und SOC2/ISO27001-Compliance — siehe Erwaegungsgrund 49). Voraussetzungen, damit die Rechtsgrundlage greift:

  • Die Datenschutzerklaerung erwaehnt den Audit-Log: IP, User-Agent, Aktion, Aufbewahrung.
  • Sensitive Payload-Felder (Passwoerter, Tokens, Secrets) werden automatisch gefiltert.
  • Eine definierte Aufbewahrungs-Obergrenze existiert (der 7-Jahres-Hard-Cleanup).
  • Die Chain liefert Manipulationsschutz — Voraussetzung fuer den forensischen Wert des Logs.

DSGVO-Loeschung

Einzelne Audit-Log-Zeilen auf eine Loeschungsanfrage hin zu loeschen wuerde die Hash-Chain brechen — der Verifier wuerde die Chain ab dieser Stelle als broken markieren. Empfohlene Vorgehensweise: Chain unangetastet lassen, nach dem Cleanup eine neue Chain (audit.chain_reset-Event) starten und die Begruendung im Datenschutzkonzept dokumentieren. Die Chain selbst kann einzelne Eintraege nicht rueckwirkend anonymisieren.

Export ​

Nutze Komponenten → CSV-Export, um die aktuelle Filteransicht als CSV herunterzuladen. Tabelle log waehlen und die gleichen Filter anwenden. Der Export selbst wird als data.export.csv (Warning) geloggt — der Audit-Trail erfasst, wer welche Daten heruntergeladen hat, inklusive Zeilenzahl und Zeitstempel.

Haeufige Fehler ​

Das Badge bleibt "Never verified" nach Klick auf Verify. Der Verifier laeuft asynchron. Der erste Lauf bei einem grossen Log dauert manchmal laenger als das 30-Sekunden-Polling-Fenster. Seite neu laden; wenn weiterhin "Never verified", das PHP-Error-Log auf [AuditChainVerifier]-Zeilen pruefen.

Ein User taucht in der Tabelle auf, ist aber unter Benutzer nicht mehr zu finden. Der User wurde wahrscheinlich geloescht. Audit-Eintraege behalten die originale User-ID — das ist der Sinn eines Audit-Trails. Match die ID gegen item.users.deleted-Events im gleichen Zeitraum, um zu finden, wann der Account entfernt wurde.

Vorher ist leer bei einem Update-Event. Der Handler, der das Webhook gefeuert hat, hat keinen before-Payload mitgegeben — der Audit-Logger sieht dann nur den neuen Stand. Item-CRUD ueber /api/backend/item liefert immer Vorher/Nachher; manche aelteren Plugin-Endpunkte liefern nur den neuen Stand. Bei Bedarf fuer ein bestimmtes Event Issue oeffnen.

Severity-Badge in der falschen Farbe. Browser-Cache. Hard-Reload (Cmd+Shift+R / Ctrl+Shift+R). Severity wird server-seitig aus dem Event-Typ berechnet und ist fix, sobald der Eintrag geschrieben ist.

Chain bricht direkt nach einem Datenbank-Restore. DB-Restores koennen TIMESTAMP-Genauigkeit oder Charset aendern — das aendert die Bytes, die in den Hash eingehen. Den Restore in einem frischen audit.chain_reset-Event dokumentieren und eine neue Chain starten.

Siehe auch ​

  • Task-Manager — der Scheduler, der den taeglichen Verifier und den Archivierer ausfuehrt.
  • Benutzer — Audit-User-IDs den Konten zuordnen.
  • API-Keys — jede Key-Mutation wird mit Critical-Severity geloggt.
  • Update-Manager — Deploys, Migrationen und Plugin-Install/Deinstallation feeden den Audit-Log.