core-kanban: Security-Folgetickets SEC-01 bis SEC-09 aus Review angelegt

Legt 9 Tickets (SEC-01..SEC-09) für verifizierte Security-Review-Befunde
(H3-H5, M5-M10) auf dem core-kanban-Board an: Revocation-Durchsetzung,
2FA-Downgrade-Schutz, resync-api Tenant-Bindung, Passwort-Policy,
kek-api Audit-Anbindung, HS256-Tenant-Bindung, Superadmin-Identitaet,
statuspage-devserver CORS, X-Forwarded-For Rate-Limit-Bypass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
patrickandClaude Sonnet 5 committed 2026-10-17 13:18:07 +02:00
1 parent b5f364fbde
commit 6d024e1d46
11 files changed
+863 -25

No files matched your search

+54 -13
View File
@@ -1,4 +1,4 @@
Code,Titel,Epic,Rolle,Komplexitaet,Aufwand_PT,Welle,Status,Technik,Beschreibung,Haengt_ab_von,Schaltet_frei,Akzeptanzkriterien,Pruefungen
Code,Titel,Epic,Rolle,Komplexitaet,Aufwand_PT,Welle,Status,Technik,Beschreibung,Haengt_ab_von,Schaltet_frei,Akzeptanzkriterien,Pruefungen
TEN-01,Tenant-Registry & Datenbank-Provisioning,🏢 Mandantenverwaltung,Backend,Mittel,4,1,Fertig,"PostgreSQL, SQL-Migrationen, Go/pgx","Schlanke Control-Plane-Registry-Datenbank (Tenant-Liste, Status, Verbindungsinformationen, Superadmin-Konten) plus automatisierte Provisioning-Routine, die für jeden neuen Mandanten eine eigene, vollständig isolierte PostgreSQL-Datenbank anlegt.",,API-01 AUD-01 CFG-01 IAM-01 LIC-01 LIC-02 TEN-02 TEN-04 TEN-06 TEN-07 TEN-09,"Registry-Datenbank enthält Tenant-Metadaten und Verbindungsinformationen, aber keine Geschäftsdaten eines Mandanten
Provisioning-Routine legt für einen neuen Mandanten eine vollständig isolierte Datenbank an und registriert sie transaktional in der Registry
Zwei Mandanten-Datenbanken sind auf Infrastrukturebene komplett getrennt – ein Zugriff über eine falsch oder fehlend gefilterte Query in einer Tenant-Datenbank kann technisch keine Zeile eines anderen Mandanten zurückliefern, weil sie nicht in derselben Datenbank existiert","Migration der Registry-Datenbank läuft gegen leere Datenbank durch und ist rückrollbar
@@ -392,62 +392,103 @@ Ein externer Modul-Client (z.B. Mail ARC-02, DMS FDN-09) kann real den Tenant-KE
Keine neue Logik - reiner Start des bestehenden Handlers","Dienst startet und bleibt stabil (systemctl status aktiv)
Realer Aufruf mit gueltigem Service-Credential liefert den erwarteten Tenant-KEK, ohne/mit falschem Credential wird abgelehnt
Code-Review: keine Aenderung an internal/kek/ selbst, nur main.go+systemd neu"
TEN-09,Registry-Härtung & Zugriffsschutz,🏢 Mandantenverwaltung,Backend,Mittel,3,9,Backlog,"PostgreSQL, Netzwerksegmentierung, dedizierte DB-Rollen, AUD-01-Kopplung","Die Control-Plane-Registry (TEN-01) hält Tenant-Liste, Verbindungsinformationen zu allen Tenant-Datenbanken sowie Superadmin-Konten – ihre Kompromittierung gefährdet alle Mandanten gleichzeitig, anders als eine einzelne Tenant-Datenbank. Registry als eigene Schutzzone härten: dedizierter Datenbank-Benutzer mit Minimalrechten für den Core-Prozess (kein Zugriff auf Tenant-Datenbanken selbst über dieselbe Rolle), Netzwerkzugriff auf die Registry-Datenbank auf den Core-Dienst beschränken, sowie ein eigenes Audit-Event für jeden lesenden und schreibenden Zugriff auf Superadmin-Konten und Verbindungsinformationen.",TEN-01 AUD-01,,"Core-Prozess greift über eine Datenbankrolle auf die Registry zu, die keine Rechte auf Tenant-Datenbanken besitzt
TEN-09,Registry-Härtung & Zugriffsschutz,🏢 Mandantenverwaltung,Backend,Mittel,3,9,Fertig,"PostgreSQL, Netzwerksegmentierung, dedizierte DB-Rollen, AUD-01-Kopplung","Die Control-Plane-Registry (TEN-01) hält Tenant-Liste, Verbindungsinformationen zu allen Tenant-Datenbanken sowie Superadmin-Konten – ihre Kompromittierung gefährdet alle Mandanten gleichzeitig, anders als eine einzelne Tenant-Datenbank. Registry als eigene Schutzzone härten: dedizierter Datenbank-Benutzer mit Minimalrechten für den Core-Prozess (kein Zugriff auf Tenant-Datenbanken selbst über dieselbe Rolle), Netzwerkzugriff auf die Registry-Datenbank auf den Core-Dienst beschränken, sowie ein eigenes Audit-Event für jeden lesenden und schreibenden Zugriff auf Superadmin-Konten und Verbindungsinformationen.",TEN-01 AUD-01,,"Core-Prozess greift über eine Datenbankrolle auf die Registry zu, die keine Rechte auf Tenant-Datenbanken besitzt
Jeder Zugriff auf Superadmin-Konten oder Verbindungsinformationen in der Registry erzeugt einen eigenen Audit-Event (Kopplung an AUD-01)
Netzwerkzugriff auf die Registry-Datenbank ist auf den Core-Dienst beschränkt, dokumentiert in der Betriebsanleitung","Testet, dass die Registry-Datenbankrolle keine Tenant-Datenbank lesen/schreiben kann (negativer Zugriffstest)
Simuliert Lesezugriff auf Superadmin-Konten und prüft, dass ein Audit-Event mit korrektem Akteur/Zeitpunkt entsteht
Prüft Firewall-/pg_hba.conf-Konfiguration gegen Zugriff von außerhalb des Core-Dienstes"
API-13,Master-Key-Rotation & Zugriffs-Audit,🔌 Core-API & Modulverwaltung,Backend,Mittel,3,9,Backlog,"KMS-Anbindung, Rotationsschnittstelle, AUD-01-Kopplung","API-10 etabliert die zweistufige Schlüsselhierarchie (Master-KEK → Tenant-KEK → Objekt-DEK), definiert aber keinen erzwungenen Rotationsprozess und kein dediziertes Audit-Ereignis für Zugriffe auf den Master-Key selbst. Eine Kompromittierung des Master-Keys hebelt die gesamte Hierarchie aus. Rotationsschnittstelle für Master-KEK und Tenant-KEKs ergänzen (Re-Envelope bestehender Tenant-KEKs bei Master-Key-Rotation, ohne Objekt-DEKs neu verschlüsseln zu müssen), sowie jeden Zugriff auf den Master-Key (Entschlüsselung eines Tenant-KEK) als eigenen, hochprivilegierten Audit-Event protokollieren.",API-10 AUD-01,,"Master-KEK-Rotation re-verschlüsselt alle Tenant-KEKs auf den neuen Master-Key, ohne Objekt-DEKs anzufassen
API-13,Master-Key-Rotation & Zugriffs-Audit,🔌 Core-API & Modulverwaltung,Backend,Mittel,3,9,Fertig,"KMS-Anbindung, Rotationsschnittstelle, AUD-01-Kopplung","API-10 etabliert die zweistufige Schlüsselhierarchie (Master-KEK → Tenant-KEK → Objekt-DEK), definiert aber keinen erzwungenen Rotationsprozess und kein dediziertes Audit-Ereignis für Zugriffe auf den Master-Key selbst. Eine Kompromittierung des Master-Keys hebelt die gesamte Hierarchie aus. Rotationsschnittstelle für Master-KEK und Tenant-KEKs ergänzen (Re-Envelope bestehender Tenant-KEKs bei Master-Key-Rotation, ohne Objekt-DEKs neu verschlüsseln zu müssen), sowie jeden Zugriff auf den Master-Key (Entschlüsselung eines Tenant-KEK) als eigenen, hochprivilegierten Audit-Event protokollieren.",API-10 AUD-01,,"Master-KEK-Rotation re-verschlüsselt alle Tenant-KEKs auf den neuen Master-Key, ohne Objekt-DEKs anzufassen
Jede Entschlüsselung eines Tenant-KEK durch den Master-Key erzeugt einen eigenen Audit-Event-Typ, sichtbar von normalen Audit-Events getrennt
Rotationsintervall ist konfigurierbar und wird bei Überschreitung als Warnung im OPS-Monitoring sichtbar","Rotationstest: Master-Key rotieren, prüfen dass alle Tenant-KEKs weiterhin entschlüsselbar sind und Objekt-DEKs unverändert bleiben
Prüft, dass jeder Tenant-KEK-Zugriff einen Audit-Event mit Akteur (Service-Credential), Zeitpunkt und betroffenem Tenant erzeugt
Prüft Alarmierung bei überschrittenem Rotationsintervall"
IAM-17,Session-/Token-Widerruf bei Logout und Kompromittierung,👤 Identität & Authentifizierung,Backend,Mittel,3,9,Backlog,"Session/JWT, Revocation-Store (PostgreSQL), Go/net/http","IAM-02 definiert Login/Session/JWT-Grundgerüst, aber kein Widerrufsmechanismus: ein ausgestelltes JWT bleibt bis zum regulären Ablauf gültig, auch nach explizitem Logout oder bei erkanntem Diebstahl. Revocation-Store ergänzen (z. B. Session-/Token-Tabelle mit Gültigkeits-Flag), den die Auth-Middleware bei jeder geschützten Anfrage zusätzlich zur Signaturprüfung konsultiert, sowie einen Admin-/Self-Service-Endpunkt zum sofortigen Widerruf einzelner oder aller Sessions eines Benutzers.",IAM-02,,"Nach explizitem Logout ist das zugehörige Token/die Session sofort ungültig, auch vor Ablauf der TTL
IAM-17,Session-/Token-Widerruf bei Logout und Kompromittierung,👤 Identität & Authentifizierung,Backend,Mittel,3,9,Fertig,"Session/JWT, Revocation-Store (PostgreSQL), Go/net/http","IAM-02 definiert Login/Session/JWT-Grundgerüst, aber kein Widerrufsmechanismus: ein ausgestelltes JWT bleibt bis zum regulären Ablauf gültig, auch nach explizitem Logout oder bei erkanntem Diebstahl. Revocation-Store ergänzen (z. B. Session-/Token-Tabelle mit Gültigkeits-Flag), den die Auth-Middleware bei jeder geschützten Anfrage zusätzlich zur Signaturprüfung konsultiert, sowie einen Admin-/Self-Service-Endpunkt zum sofortigen Widerruf einzelner oder aller Sessions eines Benutzers.",IAM-02,,"Nach explizitem Logout ist das zugehörige Token/die Session sofort ungültig, auch vor Ablauf der TTL
Ein Benutzer oder Admin kann alle aktiven Sessions eines Kontos auf einen Schlag widerrufen (z. B. bei Verdacht auf Kompromittierung)
Die Auth-Middleware prüft bei jeder Anfrage zusätzlich zur Signatur den Widerrufsstatus, mit vertretbarem Performance-Overhead (In-Prozess-Cache mit kurzer TTL zulässig)","Logout-Test: Token nach Logout gegen geschützten Endpunkt verwenden, erwartet Ablehnung
Massenwiderruf-Test: alle Sessions eines Benutzers widerrufen, prüfen dass alle zugehörigen Tokens abgelehnt werden, andere Benutzer unberührt bleiben
Lasttest der Widerrufsprüfung gegen die Auth-Middleware, um Performance-Regression auszuschließen"
IAM-18,Strikte Claim-Validierung bei OIDC-SSO,👤 Identität & Authentifizierung,Backend,Mittel,2,9,Backlog,"OpenID Connect, JWKS, Issuer-/Audience-Pinning","IAM-06 baut SSO-Anmeldung über OIDC mit Rollen-Mapping aus Claims, ohne explizit zu definieren, wie Issuer, Audience und Signatur geprüft werden. Strikte Validierung ergänzen: Issuer und Audience je Tenant fest hinterlegt statt aus dem Token selbst gelesen, Signatur ausschließlich gegen die JWKS des konfigurierten Issuers geprüft, und eine explizite Sperre, dass aus externen Claims niemals automatisch eine Superadmin-Rolle abgeleitet werden kann – Superadmin bleibt ausschließlich lokal vergebbar.",IAM-06,,"Login wird abgelehnt, wenn Issuer oder Audience des Tokens nicht exakt mit der für den Tenant hinterlegten Konfiguration übereinstimmt
IAM-18,Strikte Claim-Validierung bei OIDC-SSO,👤 Identität & Authentifizierung,Backend,Mittel,2,9,Fertig,"OpenID Connect, JWKS, Issuer-/Audience-Pinning","IAM-06 baut SSO-Anmeldung über OIDC mit Rollen-Mapping aus Claims, ohne explizit zu definieren, wie Issuer, Audience und Signatur geprüft werden. Strikte Validierung ergänzen: Issuer und Audience je Tenant fest hinterlegt statt aus dem Token selbst gelesen, Signatur ausschließlich gegen die JWKS des konfigurierten Issuers geprüft, und eine explizite Sperre, dass aus externen Claims niemals automatisch eine Superadmin-Rolle abgeleitet werden kann – Superadmin bleibt ausschließlich lokal vergebbar.",IAM-06,,"Login wird abgelehnt, wenn Issuer oder Audience des Tokens nicht exakt mit der für den Tenant hinterlegten Konfiguration übereinstimmt
Signaturprüfung verwendet ausschließlich die JWKS des konfigurierten Issuers, nie einen im Token selbst mitgelieferten Schlüssel/Issuer
Rollen-Mapping aus externen Claims kann technisch keine Superadmin-Rolle vergeben, unabhängig vom Claim-Inhalt","Negativtest: Token mit falschem Issuer/Audience wird abgelehnt
Negativtest: manipuliertes Token mit fremdem Signaturschlüssel wird abgelehnt
Negativtest: externer Claim behauptet Superadmin-Rolle, Login vergibt trotzdem höchstens die höchste zulässige tenant-scoped Rolle"
RBAC-08,Testfall gegen Tenant-zu-Superadmin-Rechteeskalation,🔐 Rollen & Rechte,QA,Einfach,1,9,Backlog,"Automatisierte Tests, Casbin-Policy-Prüfung","RBAC-01 definiert das Rollenmodell mit Rollenhierarchie, aber kein Ticket prüft explizit, dass ein Tenant-Admin über Rollenvererbung oder Policy-Fehlkonfiguration niemals den Superadmin-Scope erreichen kann. Verbindlichen automatisierten Testfall (Teil der QA-Prüfgates) ergänzen, der jede Rollen-/Policy-Änderung gegen diese Invariante prüft.",RBAC-01,,"Automatisierter Test schlägt fehl, sobald eine Rollen-/Policy-Änderung einem Tenant-Admin (oder abgeleiteten Rollen) Superadmin-Rechte einräumt
RBAC-08,Testfall gegen Tenant-zu-Superadmin-Rechteeskalation,🔐 Rollen & Rechte,QA,Einfach,1,9,Fertig,"Automatisierte Tests, Casbin-Policy-Prüfung","RBAC-01 definiert das Rollenmodell mit Rollenhierarchie, aber kein Ticket prüft explizit, dass ein Tenant-Admin über Rollenvererbung oder Policy-Fehlkonfiguration niemals den Superadmin-Scope erreichen kann. Verbindlichen automatisierten Testfall (Teil der QA-Prüfgates) ergänzen, der jede Rollen-/Policy-Änderung gegen diese Invariante prüft.",RBAC-01,,"Automatisierter Test schlägt fehl, sobald eine Rollen-/Policy-Änderung einem Tenant-Admin (oder abgeleiteten Rollen) Superadmin-Rechte einräumt
Test läuft als Pflichtgate in der CI bei jeder Änderung an Rollenmodell oder Policy-Definitionen
Testfall ist dokumentiert und Teil der QA-Prüfgates (Referenz aus QA-03/QA-05)","Test gezielt mit einer absichtlich fehlerhaften Policy (Tenant-Admin erbt Superadmin) ausführen, muss fehlschlagen
Test gegen die aktuelle, korrekte Policy ausführen, muss erfolgreich durchlaufen"
AUD-07,Technische Durchsetzung Append-only Audit-Log,📜 Audit & Compliance,Backend,Einfach,2,9,Backlog,"PostgreSQL (REVOKE/Trigger), Go","AUD-01 konzipiert das Audit-Log als append-only, setzt dies aber nur als Konvention um – ein Datenbank-Superuser oder eine kompromittierte Anwendungsrolle könnte Einträge weiterhin ändern oder löschen. Append-only technisch erzwingen: der Anwendungs-Datenbankrolle werden UPDATE- und DELETE-Rechte auf die Audit-Tabelle vollständig entzogen (nur INSERT/SELECT), ergänzt um einen Datenbank-Trigger, der jeden Änderungsversuch protokolliert und ablehnt.",AUD-01,,"Die vom Core-Prozess verwendete Datenbankrolle kann Audit-Einträge nachweislich weder verändern noch löschen (fehlende Rechte, nicht nur fehlender Code-Pfad)
AUD-07,Technische Durchsetzung Append-only Audit-Log,📜 Audit & Compliance,Backend,Einfach,2,9,Fertig,"PostgreSQL (REVOKE/Trigger), Go","AUD-01 konzipiert das Audit-Log als append-only, setzt dies aber nur als Konvention um – ein Datenbank-Superuser oder eine kompromittierte Anwendungsrolle könnte Einträge weiterhin ändern oder löschen. Append-only technisch erzwingen: der Anwendungs-Datenbankrolle werden UPDATE- und DELETE-Rechte auf die Audit-Tabelle vollständig entzogen (nur INSERT/SELECT), ergänzt um einen Datenbank-Trigger, der jeden Änderungsversuch protokolliert und ablehnt.",AUD-01,,"Die vom Core-Prozess verwendete Datenbankrolle kann Audit-Einträge nachweislich weder verändern noch löschen (fehlende Rechte, nicht nur fehlender Code-Pfad)
Ein Versuch, einen Audit-Eintrag zu ändern oder zu löschen, wird von der Datenbank abgelehnt und selbst protokolliert
Bestehende AUD-01-Funktionalität (Schreiben/Lesen neuer Einträge) bleibt unverändert nutzbar","Testet UPDATE/DELETE gegen die Audit-Tabelle mit der Anwendungsrolle, erwartet Datenbankfehler (Rechteverweigerung)
Prüft, dass der Ablehnungsversuch selbst sichtbar protokolliert wird
Regressionstest: normales Schreiben/Lesen von Audit-Einträgen funktioniert weiterhin"
TEN-10,Integritätsprüfung von SQL-Migrationen vor Tenant-Rollout,🏢 Mandantenverwaltung,Backend,Mittel,2,10,Backlog,"PostgreSQL, SQL-Migrationen, Checksummen/Signaturprüfung, Go","TEN-07 rollt Migrationen automatisiert gegen jede Tenant-Datenbank aus und behandelt Fehler pro Tenant, prüft aber nicht die Integrität der Migrationsdatei selbst vor dem Rollout. Eine manipulierte oder versehentlich falsche Migrationsdatei würde sonst automatisch gegen JEDE Tenant-Datenbank angewendet – 1:N-Risiko statt 1:1. Checksummenprüfung (z. B. SHA-256) je Migrationsdatei ergänzen, die vor dem ersten Anwenden gegen eine hinterlegte, versionierte Prüfsumme im Migrations-Manifest abgeglichen wird; bei Abweichung wird der gesamte Rollout abgebrochen, bevor auch nur eine Tenant-Datenbank betroffen ist.",TEN-07,,"Jede Migrationsdatei hat einen Eintrag mit Prüfsumme im Migrations-Manifest, der vor dem Rollout erzeugt/versioniert wird
TEN-10,Integritätsprüfung von SQL-Migrationen vor Tenant-Rollout,🏢 Mandantenverwaltung,Backend,Mittel,2,10,Fertig,"PostgreSQL, SQL-Migrationen, Checksummen/Signaturprüfung, Go","TEN-07 rollt Migrationen automatisiert gegen jede Tenant-Datenbank aus und behandelt Fehler pro Tenant, prüft aber nicht die Integrität der Migrationsdatei selbst vor dem Rollout. Eine manipulierte oder versehentlich falsche Migrationsdatei würde sonst automatisch gegen JEDE Tenant-Datenbank angewendet – 1:N-Risiko statt 1:1. Checksummenprüfung (z. B. SHA-256) je Migrationsdatei ergänzen, die vor dem ersten Anwenden gegen eine hinterlegte, versionierte Prüfsumme im Migrations-Manifest abgeglichen wird; bei Abweichung wird der gesamte Rollout abgebrochen, bevor auch nur eine Tenant-Datenbank betroffen ist.",TEN-07,,"Jede Migrationsdatei hat einen Eintrag mit Prüfsumme im Migrations-Manifest, der vor dem Rollout erzeugt/versioniert wird
Der Rollout gegen alle Tenant-Datenbanken startet nur, wenn die tatsächliche Prüfsumme der Migrationsdatei mit dem Manifest-Eintrag übereinstimmt
Bei Prüfsummen-Abweichung wird der Rollout vollständig abgebrochen, bevor eine einzige Tenant-Datenbank verändert wurde, und es entsteht ein Audit-Event","Test mit unveränderter Migrationsdatei: Rollout läuft normal durch
Test mit nachträglich manipulierter Migrationsdatei (Prüfsumme weicht ab): Rollout bricht vor der ersten Tenant-Datenbank ab
Prüft, dass der Abbruch einen nachvollziehbaren Audit-Event erzeugt (wer/was/wann)"
TEN-11,Deckelung & Rate-Limit für Tenant-Connection-Pooling,🏢 Mandantenverwaltung,Backend,Mittel,2,10,Backlog,"Go/pgx, LRU-Pool, Rate-Limiting",TEN-06 löst Tenant-Datenbankverbindungen per LRU-verwaltetem Pool auf. Ohne Obergrenze kann ein fehlerhafter oder böswilliger Client durch häufigen Tenant-Wechsel ständiges Verbindungsauf- und -abbauen erzwingen (Pool-Thrashing) und damit die Datenbank-Verbindungslast für alle anderen Mandanten erhöhen. Eine harte Obergrenze für gleichzeitig offene Pools sowie ein Rate-Limit auf die Tenant-Wechsel-Rate pro Client/Service-Credential ergänzen.,TEN-06,,"Die Anzahl gleichzeitig offener Tenant-Connection-Pools ist konfigurierbar gedeckelt
TEN-11,Deckelung & Rate-Limit für Tenant-Connection-Pooling,🏢 Mandantenverwaltung,Backend,Mittel,2,10,Fertig,"Go/pgx, LRU-Pool, Rate-Limiting",TEN-06 löst Tenant-Datenbankverbindungen per LRU-verwaltetem Pool auf. Ohne Obergrenze kann ein fehlerhafter oder böswilliger Client durch häufigen Tenant-Wechsel ständiges Verbindungsauf- und -abbauen erzwingen (Pool-Thrashing) und damit die Datenbank-Verbindungslast für alle anderen Mandanten erhöhen. Eine harte Obergrenze für gleichzeitig offene Pools sowie ein Rate-Limit auf die Tenant-Wechsel-Rate pro Client/Service-Credential ergänzen.,TEN-06,,"Die Anzahl gleichzeitig offener Tenant-Connection-Pools ist konfigurierbar gedeckelt
Ein einzelner Client/Service-Credential kann die Tenant-Wechsel-Rate nicht über ein konfiguriertes Limit hinaus treiben, ohne abgelehnt/gedrosselt zu werden
Bei Erreichen der Obergrenze wird der am längsten unbenutzte Pool verdrängt (LRU), keine Anfrage schlägt hart fehl, solange das Wechsel-Rate-Limit eingehalten wird","Lasttest mit vielen parallelen Tenant-Wechseln eines einzelnen Clients: Rate-Limit greift, andere Mandanten bleiben unbeeinträchtigt
Test der LRU-Verdrängung bei Erreichen der Pool-Obergrenze
Regressionstest: normaler Betrieb mit wenigen Mandanten bleibt unverändert performant"
IAM-19,Verschlüsselte Ablage von LDAP/AD-Bind-Credentials,👤 Identität & Authentifizierung,Backend,Mittel,2,10,Backlog,"LDAP/AD, Envelope-Encryption (Tenant-KEK aus API-10), PostgreSQL","IAM-05 bindet je Tenant an LDAP/Active Directory an, ohne festzulegen, wie das Bind-Passwort gespeichert wird. Bind-Credentials über den Tenant-KEK aus der Schlüsselhierarchie (API-10) verschlüsseln statt in einer separaten, eigenen Secret-Verwaltung oder gar Klartext in der Tenant-Konfiguration abzulegen.",IAM-05 API-10,,"LDAP-Bind-Passwort ist in der Datenbank ausschließlich verschlüsselt gespeichert, entschlüsselt mit dem Tenant-KEK des jeweiligen Mandanten
IAM-19,Verschlüsselte Ablage von LDAP/AD-Bind-Credentials,👤 Identität & Authentifizierung,Backend,Mittel,2,10,Fertig,"LDAP/AD, Envelope-Encryption (Tenant-KEK aus API-10), PostgreSQL","IAM-05 bindet je Tenant an LDAP/Active Directory an, ohne festzulegen, wie das Bind-Passwort gespeichert wird. Bind-Credentials über den Tenant-KEK aus der Schlüsselhierarchie (API-10) verschlüsseln statt in einer separaten, eigenen Secret-Verwaltung oder gar Klartext in der Tenant-Konfiguration abzulegen.",IAM-05 API-10,,"LDAP-Bind-Passwort ist in der Datenbank ausschließlich verschlüsselt gespeichert, entschlüsselt mit dem Tenant-KEK des jeweiligen Mandanten
Ein direkter Lesezugriff auf die Konfigurationstabelle liefert keinen verwertbaren Klartext des Bind-Passworts
Bestehende LDAP-Synchronisation/Login-Delegation aus IAM-05 funktioniert unverändert","Test: LDAP-Bind-Passwort in der Datenbank direkt auslesen, prüfen dass nur Chiffrat sichtbar ist
Regressionstest: LDAP-Login/Synchronisation funktioniert nach Umstellung auf verschlüsselte Ablage weiterhin korrekt
Test: Entschlüsselung schlägt fehl, wenn der falsche Tenant-KEK verwendet wird (kein Cross-Tenant-Zugriff auf Bind-Credentials)"
LIC-06,Fail-Closed-Verhalten der Lizenz-/Feature-Flag-Prüfung bei Registry-Ausfall,🎫 Lizenzierung & Feature Flags,Backend,Einfach,1,10,Backlog,"PostgreSQL, In-Prozess-Cache, Go","LIC-01/LIC-02 prüfen Lizenz und Feature-Flags gegen die Registry mit In-Prozess-Cache, ohne explizit festzulegen, wie sich die Prüfung bei einem Ausfall der Registry-Datenbank oder einem Cache-Invalidierungssturm verhält. Verbindliches Fail-Closed-Verhalten für die Lizenzprüfung festlegen und implementieren: ist weder Cache noch Registry erreichbar, wird der Zugriff verweigert statt stillschweigend erlaubt; für reine Feature-Flags (nicht lizenzrelevant) ist ein definiertes, dokumentiertes Fallback-Verhalten (letzter bekannter Cache-Stand mit Ablaufzeit) zulässig.",LIC-01 LIC-02,,"Ist bei einer Lizenzprüfung weder der In-Prozess-Cache noch die Registry erreichbar, wird der Zugriff verweigert (Fail-Closed), nicht stillschweigend erlaubt
LIC-06,Fail-Closed-Verhalten der Lizenz-/Feature-Flag-Prüfung bei Registry-Ausfall,🎫 Lizenzierung & Feature Flags,Backend,Einfach,1,10,Fertig,"PostgreSQL, In-Prozess-Cache, Go","LIC-01/LIC-02 prüfen Lizenz und Feature-Flags gegen die Registry mit In-Prozess-Cache, ohne explizit festzulegen, wie sich die Prüfung bei einem Ausfall der Registry-Datenbank oder einem Cache-Invalidierungssturm verhält. Verbindliches Fail-Closed-Verhalten für die Lizenzprüfung festlegen und implementieren: ist weder Cache noch Registry erreichbar, wird der Zugriff verweigert statt stillschweigend erlaubt; für reine Feature-Flags (nicht lizenzrelevant) ist ein definiertes, dokumentiertes Fallback-Verhalten (letzter bekannter Cache-Stand mit Ablaufzeit) zulässig.",LIC-01 LIC-02,,"Ist bei einer Lizenzprüfung weder der In-Prozess-Cache noch die Registry erreichbar, wird der Zugriff verweigert (Fail-Closed), nicht stillschweigend erlaubt
Für reine Feature-Flag-Abfragen (nicht lizenzrelevant) ist das Fallback-Verhalten dokumentiert und über einen Ablaufzeitpunkt des letzten Cache-Stands begrenzt
Ein Cache-Invalidierungssturm bei Massenänderung führt nicht zu kurzzeitig falsch erteilten Lizenz-Freigaben","Test: Registry-Datenbank simuliert nicht erreichbar, Cache leer/abgelaufen — Lizenzprüfung verweigert Zugriff
Test: Massenänderung an Feature-Flags gleichzeitig mit vielen Anfragen — keine falsch-positive Freigabe während der Invalidierung
Regressionstest: normaler Betrieb mit erreichbarer Registry bleibt unverändert"
OPS-07,Betriebs-Gate: mTLS-Aktivierungspflicht beim Wechsel auf verteilten Betrieb,📈 Monitoring & Betrieb,Backend,Einfach,1,10,Backlog,"Betriebsdokument, Health-/Readiness-Check, mTLS (API-09)","API-09 macht mTLS zwischen Modul-Instanzen für Stufe-2/3-Installationen (siehe SKALIERUNGSKONZEPT.md) verfügbar, aber für Stufe-1-Einzel-LXC optional. Ohne expliziten Gate-Mechanismus kann beim späteren Wechsel von Stufe 1 auf Stufe 2 (verteilter Betrieb) vergessen werden, mTLS zu aktivieren — Modul-zu-Modul-Aufrufe liefen dann nur mit Service-Credential ohne Transport-Auth. Einen Readiness-Check ergänzen, der bei mehr als einer Instanz/Host erkennt, dass mTLS zwischen Modulen (noch) nicht aktiv ist, und dies als Warnung im Monitoring (OPS) sichtbar macht, sowie einen Eintrag in der Betriebs-Checkliste für den Stufenwechsel.",API-09,,"Wird mehr als eine Modul-Instanz/mehr als ein Host erkannt und ist mTLS zwischen Modulen nicht aktiv, erscheint eine Warnung im Monitoring (OPS)
OPS-07,Betriebs-Gate: mTLS-Aktivierungspflicht beim Wechsel auf verteilten Betrieb,📈 Monitoring & Betrieb,Backend,Einfach,1,10,Fertig,"Betriebsdokument, Health-/Readiness-Check, mTLS (API-09)","API-09 macht mTLS zwischen Modul-Instanzen für Stufe-2/3-Installationen (siehe SKALIERUNGSKONZEPT.md) verfügbar, aber für Stufe-1-Einzel-LXC optional. Ohne expliziten Gate-Mechanismus kann beim späteren Wechsel von Stufe 1 auf Stufe 2 (verteilter Betrieb) vergessen werden, mTLS zu aktivieren — Modul-zu-Modul-Aufrufe liefen dann nur mit Service-Credential ohne Transport-Auth. Einen Readiness-Check ergänzen, der bei mehr als einer Instanz/Host erkennt, dass mTLS zwischen Modulen (noch) nicht aktiv ist, und dies als Warnung im Monitoring (OPS) sichtbar macht, sowie einen Eintrag in der Betriebs-Checkliste für den Stufenwechsel.",API-09,,"Wird mehr als eine Modul-Instanz/mehr als ein Host erkannt und ist mTLS zwischen Modulen nicht aktiv, erscheint eine Warnung im Monitoring (OPS)
Betriebs-Checkliste für den Wechsel Stufe 1 → Stufe 2 (SKALIERUNGSKONZEPT.md) enthält mTLS-Aktivierung als Pflichtschritt
Bei aktivem mTLS und/oder Einzel-LXC-Betrieb erscheint keine Warnung","Simulierter Mehr-Instanz-Betrieb ohne mTLS: Warnung erscheint im Monitoring
Simulierter Mehr-Instanz-Betrieb mit aktivem mTLS: keine Warnung
Prüft, dass die Betriebs-Checkliste den mTLS-Schritt beim Stufenwechsel enthält"
IAM-20,Anomalie-Erkennung bei API-Token-/Service-Account-Nutzung,👤 Identität & Authentifizierung,Backend,Mittel,2,11,Backlog,"PostgreSQL, IP-/Rate-Heuristik, Core-Audit-Kopplung (AUD-01)","IAM-09 deckt Scopes und Ablaufzeit für API-Token/Service-Accounts ab, aber ein gestohlenes, noch gültiges Token bleibt bis zum Ablauf unauffällig nutzbar. Einfache Anomalie-Erkennung ergänzen: Nutzung eines Tokens aus einer bisher nicht gesehenen IP-Range oder mit einer gegenüber dem bisherigen Muster stark abweichenden Zugriffsrate erzeugt einen Audit-Event mit erhöhtem Schweregrad (Kopplung an AUD-01) und optional eine Benachrichtigung an den Token-Besitzer/Tenant-Admin über den Core-Dispatcher (CFG-02). Keine automatische Sperre – die Erkennung ist ein Hinweissignal, keine eigenständige Durchsetzung, um Fehlalarme nicht zu blockierenden Vorfällen zu machen.",IAM-09 AUD-01,,"Nutzung eines API-Tokens/Service-Accounts aus einer bisher nicht beobachteten IP-Range erzeugt einen Audit-Event mit erhöhtem Schweregrad
IAM-20,Anomalie-Erkennung bei API-Token-/Service-Account-Nutzung,👤 Identität & Authentifizierung,Backend,Mittel,2,11,Fertig,"PostgreSQL, IP-/Rate-Heuristik, Core-Audit-Kopplung (AUD-01)","IAM-09 deckt Scopes und Ablaufzeit für API-Token/Service-Accounts ab, aber ein gestohlenes, noch gültiges Token bleibt bis zum Ablauf unauffällig nutzbar. Einfache Anomalie-Erkennung ergänzen: Nutzung eines Tokens aus einer bisher nicht gesehenen IP-Range oder mit einer gegenüber dem bisherigen Muster stark abweichenden Zugriffsrate erzeugt einen Audit-Event mit erhöhtem Schweregrad (Kopplung an AUD-01) und optional eine Benachrichtigung an den Token-Besitzer/Tenant-Admin über den Core-Dispatcher (CFG-02). Keine automatische Sperre – die Erkennung ist ein Hinweissignal, keine eigenständige Durchsetzung, um Fehlalarme nicht zu blockierenden Vorfällen zu machen.",IAM-09 AUD-01,,"Nutzung eines API-Tokens/Service-Accounts aus einer bisher nicht beobachteten IP-Range erzeugt einen Audit-Event mit erhöhtem Schweregrad
Ein gegenüber dem historischen Muster stark abweichender Zugriffsratenanstieg desselben Tokens erzeugt ebenfalls einen Audit-Event
Das Token bleibt bei erkannter Anomalie weiterhin gültig (keine automatische Sperre) – die Erkennung dient als Hinweissignal, tatsächliche Sperre erfolgt manuell oder über einen bewussten separaten Prozess","Test: Zugriff mit einem Token aus einer neuen IP-Range erzeugt den erwarteten Audit-Event
Test: künstlich stark erhöhte Zugriffsrate desselben Tokens erzeugt den erwarteten Audit-Event
Regressionstest: normale, unauffällige Token-Nutzung erzeugt keine Falsch-Alarme im regulären Betrieb"
SEC-01,Verbindliche Session-Revocation-Pruefung auf allen RequireAuth-Routen,🔒 Sicherheit,Backend,Mittel,2,12,Backlog,"internal/auth/middleware.go, internal/auth/token.go, cmd/account-api, cmd/rbac-admin-api, cmd/notifyprefs-api","In `internal/auth/middleware.go:26,40` wird der Revocation-Status eines Tokens nur geprüft, wenn der aufrufende Code explizit einen `revocationStore`/Checker übergibt — Standardverhalten ist ""nicht prüfen"". In `cmd/account-api/main.go:212-217` registrieren `/account/me`, `/account/change-password` und `/auth/totp/*` `RequireAuth` ohne diesen Checker (nur `moduletoken.RegisterToken` in Zeile 203 übergibt ihn). `cmd/rbac-admin-api/main.go:73-80` und `cmd/notifyprefs-api/main.go:71-73` haben dieselbe Lücke. Zusammen mit der 90-Tage-Token-Laufzeit (`internal/auth/token.go:221`, `MobileAccessTokenTTL`) bedeutet das: ein gestohlenes Token bleibt nach Logout UND nach einem Passwortwechsel (der `RevokeAllForUser` auslöst) bis zu 90 Tage lang gültig für Profilzugriff, TOTP-Setup und die komplette RBAC-Admin-API.",,,"`RequireAuth` prüft den Revocation-Status immer, wenn ein Revocation-Store im Prozess konfiguriert ist — kein optionaler Parameter mit Default ""nicht prüfen"" mehr; fehlt ein Store, scheitert der Prozessstart statt den Check stillschweigend zu überspringen
`cmd/account-api/main.go` übergibt an allen `RequireAuth`-Aufrufen (`/account/me`, `/account/change-password`, `/auth/totp/*`) denselben Revocation-Store wie `moduletoken.RegisterToken`
`cmd/rbac-admin-api/main.go` und `cmd/notifyprefs-api/main.go` übergeben ebenfalls den Revocation-Store an jeden `RequireAuth`-Aufruf
Ein nach Logout oder Passwortwechsel widerrufenes Token wird auf jeder dieser Routen mit 401 abgelehnt, nicht erst nach Ablauf der 90-Tage-TTL","Token ausstellen, Logout auslösen, danach `/account/me` mit demselben Token aufrufen — vor dem Fix: 200, nach dem Fix: 401, Nachweis per Request/Response-Log
Token ausstellen, Passwortwechsel auslösen (`RevokeAllForUser`), danach `/auth/totp/setup/begin` und einen RBAC-Admin-Endpunkt mit dem alten Token aufrufen — beide müssen 401 liefern
Architekturtest (analog API-21/INT-15-Muster), der einen `RequireAuth`-Aufruf ohne Revocation-Store im Build scheitern lässt, mit absichtlich eingefügtem Verstoß verifiziert und danach entfernt"
SEC-02,Re-Authentifizierung vor TOTP-Setup/-Reset erzwingen,🔒 Sicherheit,Backend,Mittel,2,12,Backlog,"internal/totp/handler.go, internal/totp/store.go","`internal/totp/store.go:99-102` setzt per `ON CONFLICT (user_id) DO UPDATE SET secret = NULL, secret_ciphertext=$2, confirmed=false` die 2FA eines Nutzers zurück. `SetupBegin` in `internal/totp/handler.go` (ca. Zeile 164-185) verlangt dafür weder das aktuelle Passwort noch einen gültigen TOTP-Code — die Route liegt nur hinter `RequireAuth`. Damit kann `POST /auth/totp/setup/begin` mit einer gestohlenen Session (z. B. XSS oder Session-Cookie-Diebstahl) sofort `IsEnabled=false` setzen und die 2FA des Opfers deaktivieren, ohne dass bestehende Recovery-Codes invalidiert werden.",,,"`SetupBegin` (und jede andere Route, die `confirmed=false`/`secret=NULL` setzen kann) verlangt zusätzlich zur gültigen Session eine frische Re-Authentifizierung: aktuelles Passwort ODER einen gültigen bestehenden TOTP-Code
Fehlt die Re-Authentifizierung oder ist sie falsch, bleibt der bestehende TOTP-Zustand (inklusive Recovery-Codes) unverändert und die Anfrage wird mit 401/403 abgelehnt
Ein erfolgreicher TOTP-Reset invalidiert die bisherigen Recovery-Codes explizit, statt sie unangetastet weiterbestehen zu lassen","`POST /auth/totp/setup/begin` nur mit gültiger Session und ohne Passwort/TOTP-Code aufrufen — muss vor dem Fix durchgehen (2FA wird deaktiviert) und nach dem Fix mit 401/403 scheitern, 2FA bleibt aktiv
Gleicher Aufruf mit korrektem aktuellem Passwort — muss erfolgreich das Setup beginnen
Nach erfolgreichem Reset: alte Recovery-Codes gegen den Recovery-Login-Endpunkt testen — müssen abgelehnt werden"
SEC-03,Modul-Tenant-Bindung und Delta-Validierung in resync-api,🔒 Sicherheit,Backend,Mittel,2,12,Backlog,"internal/resync/handler.go, internal/kek/handler.go (Referenzmuster)","`internal/resync/handler.go:52-60` verwirft den von `Authenticate` zurückgegebenen Modulnamen, statt ihn weiterzuverwenden. Zeile 90 ruft `h.audit.Record(ctx, e.TenantSlug, e.Actor, e.Action, e.Target, e.Metadata, e.OccurredAt)` mit `TenantSlug` direkt aus dem Request-Body auf; Zeile 120 ruft `usage.Increment(ctx, d.TenantSlug, d.Metric, d.Delta)` mit einem beliebigen `int64 Delta` auf, das auch negativ sein kann. Ein `IsActive(tenant, module)`-Check wie in `internal/kek/handler.go:853-869` fehlt komplett. Damit kann ein kompromittiertes Modul-Credential rückdatierte, nicht löschbare Audit-Einträge für fremde Mandanten erzeugen oder `storage_bytes` per negativem Delta künstlich auf 0 setzen (Quota-Umgehung).",API-13,,"Vor jedem `audit.Record`- und `usage.Increment`-Aufruf prüft der Handler per `IsActive(tenant, module)` (analog kek-Muster), dass das authentifizierte Modul-Credential für genau diesen `TenantSlug` aktiv zugelassen ist
`TenantSlug` aus dem Request-Body wird gegen die tatsächliche Modul-Tenant-Zuordnung des Credentials validiert, nicht blind übernommen — Abweichung führt zu Ablehnung (4xx), nicht zu stillschweigender Korrektur
`Delta` in Usage-Increment-Requests wird auf einen sinnvollen Wertebereich begrenzt (kein beliebig negativer Wert, der eine Quota auf 0 oder negativ setzen kann); unzulässige Werte werden abgelehnt","Request mit gültigem Modul-Credential für Mandant A, aber `TenantSlug` von Mandant B im Body — vor dem Fix: Audit-Eintrag für B wird geschrieben, nach dem Fix: Ablehnung, kein Eintrag für B
Usage-Increment mit stark negativem `Delta` gegen eine bestehende Quota von Mandant A — vor dem Fix: Quota wird auf 0 oder negativ gesetzt, nach dem Fix: Ablehnung oder Begrenzung auf plausiblen Bereich
Regulärer Resync-Vorgang eines zugelassenen Moduls für den eigenen Mandanten bleibt nach dem Fix unverändert funktionsfähig"
SEC-04,Passwort-Policy-Durchsetzung und Session-Widerruf nach Reset,🔒 Sicherheit,Backend,Mittel,2,12,Backlog,"internal/pwpolicy/policy.go, internal/authtoken/flow.go, internal/auth/changepassword.go","`internal/pwpolicy/policy.go:36` definiert `Validate`, hat aber laut Grep null Aufrufer im gesamten Repository — die Passwort-Policy wird nirgends angewendet. `internal/authtoken/flow.go:31-36` und `internal/auth/changepassword.go:79` übernehmen ein neues Passwort ohne vorherigen Policy-Check. Zusätzlich wird nach einem Passwort-Reset `RevokeAllForUser` nicht aufgerufen — bestehende Sessions und Tokens bleiben nach einem Reset gültig (verschärft durch die 90-Tage-TTL aus SEC-01).",SEC-01,,"Jede Stelle, die ein neues Klartext-Passwort übernimmt (Setup, Change, Reset), ruft vorher `pwpolicy.Validate` auf; ein die Policy verletzendes Passwort wird mit klarer Fehlermeldung abgelehnt
Nach erfolgreichem Passwort-Reset (`authtoken`-Flow) ruft der Handler `RevokeAllForUser` für den betroffenen Nutzer auf
Nach erfolgreichem Passwort-Change (`changepassword.go`) gilt dieselbe Widerrufspflicht bereits (Bestandscode prüfen) oder wird ergänzt, falls dort ebenfalls eine Lücke besteht","Passwort-Setup/-Change/-Reset mit einem Policy-verletzenden Passwort (z. B. zu kurz) — muss vor dem Fix durchgehen und nach dem Fix abgelehnt werden
Vor Reset ein Token/Session ausstellen, Reset durchführen, danach das alte Token gegen eine geschützte Route prüfen — muss nach dem Fix 401 liefern
Gültiges, policy-konformes Passwort wird nach dem Fix weiterhin erfolgreich übernommen"
SEC-05,Audit-Anbindung des kek-api-Einstiegspunkts aktivieren,🔒 Sicherheit,Backend,Einfach,1,12,Backlog,"cmd/kek-api/main.go, internal/kek/store.go","`cmd/kek-api/main.go:71` instanziiert `kek.NewStore(pool)` ohne `.WithAudit`. `internal/kek/store.go:231` schreibt den Eintrag `master_key_access` nur, wenn `s.audit != nil` ist — in `kek-api` ist das nie der Fall. Das verletzt die bereits in API-13 (Akzeptanzkriterium 2, Audit-Pflicht für Master-Key-Zugriffe) festgelegte Anforderung: Master-Key-Zugriffe über diesen Einstiegspunkt werden aktuell überhaupt nicht protokolliert.",API-13,,"`cmd/kek-api/main.go` instanziiert `kek.NewStore` mit `.WithAudit`, unter Verwendung derselben Audit-Anbindung wie andere Core-Dienste
Jeder Master-Key-Zugriff über `kek-api` erzeugt nachweislich einen `master_key_access`-Audit-Eintrag mit Akteur, Zeitpunkt und betroffenem Tenant","Master-Key-Zugriff über `kek-api` auslösen, danach Audit-Log abfragen — vor dem Fix: kein Eintrag, nach dem Fix: `master_key_access`-Eintrag mit korrektem Akteur/Tenant/Zeitpunkt vorhanden
Bestehende API-13-Prüfung (Audit-Event bei Tenant-KEK-Entschlüsselung) erneut gegen `kek-api` ausführen und grün bestätigen"
SEC-06,Tenant-Bindung in RequireAuth bei geteiltem HS256-Secret,🔒 Sicherheit,Backend,Mittel,2,12,Backlog,"internal/auth/middleware.go, internal/moduletoken/token.go (Referenzmuster), cmd/rbac-admin-api/main.go","`internal/auth/middleware.go:26-56` prüft im HS256-Pfad keine Tenant-Bindung des Tokens — anders als `internal/moduletoken/token.go:316`, das dies korrekt tut. Relevant wird das konkret bei `cmd/rbac-admin-api/main.go:43` (`NEXARCH_RBACADMIN_JWT_SECRET`): falls dieses Secret zwischen mehreren Mandanten oder Instanzen geteilt wird, könnte ein für Mandant A ausgestelltes Token auch für Mandant B akzeptiert werden, da `RequireAuth` die Tenant-Zugehörigkeit nicht gegen die aufgerufene Instanz verifiziert.",,,"`RequireAuth` prüft im HS256-Pfad die Tenant-Bindung des Tokens gegen die erwartete Tenant-Instanz, nach demselben Muster wie `moduletoken.Verify` (token.go:316)
Ein für Mandant A ausgestelltes Token wird von einer für Mandant B konfigurierten Instanz abgelehnt, auch wenn dasselbe HS256-Secret verwendet wird
Dokumentiert (Code-Kommentar oder Config-Validierung beim Start), dass das JWT-Secret strikt pro Mandant/Instanz unterschiedlich sein muss, falls die Tenant-Bindungsprüfung aus technischen Gründen nicht vollständig greift","Token für Mandant A mit geteiltem Secret ausstellen, gegen eine für Mandant B konfigurierte `RequireAuth`-Instanz verwenden — vor dem Fix: Zugriff gewährt, nach dem Fix: 401
Reguläres Token für die eigene Tenant-Instanz bleibt nach dem Fix gültig"
SEC-07,Superadmin-Identität aus verifiziertem Claim statt Query-Parameter,🔒 Sicherheit,Backend,Mittel,2,12,Backlog,"internal/tenantadmin/tenantadmin.go, internal/tenantadmin/tenantimportdirapi.go","`internal/tenantadmin/tenantadmin.go:225,231,251,273` liest `q.Get(""superadmin"")` direkt aus dem Query-String; `tenantimportdirapi.go:64,76` macht dasselbe. Jeder Inhaber eines gültigen Service-Tokens kann damit den im Audit-Log protokollierten Akteurs-Namen frei als beliebigen ""Superadmin"" fälschen — der Audit-Trail für hochprivilegierte Tenant-Admin-Operationen ist dadurch nicht vertrauenswürdig.",,,"Alle vier Stellen in `tenantadmin.go` (Zeilen 225, 231, 251, 273) und beide Stellen in `tenantimportdirapi.go` (Zeilen 64, 76) beziehen die Superadmin-Identität aus einem verifizierten Claim des Service-Tokens, nicht aus `q.Get(""superadmin"")`
Ein Request mit abweichendem `superadmin`-Query-Parameter wird entweder ignoriert (verifizierter Claim gewinnt) oder mit 4xx abgelehnt, nie stillschweigend als Akteursname übernommen
Audit-Einträge für diese Operationen enthalten nachweislich den aus dem Token verifizierten Namen, nicht den Query-Parameter","Service-Token für Superadmin X verwenden, aber `?superadmin=Y` im Query-String setzen — vor dem Fix: Audit-Eintrag nennt Y, nach dem Fix: Audit-Eintrag nennt X (oder Request wird abgelehnt)
Request ohne gültiges Service-Token mit beliebigem `superadmin`-Parameter — muss abgelehnt werden
Regulärer Superadmin-Workflow mit korrektem Token bleibt funktionsfähig, Audit-Eintrag stimmt mit tatsächlichem Akteur überein"
SEC-08,statuspage-devserver: offenes CORS ohne Auth aus systemd entfernen oder absichern,🔒 Sicherheit,Backend,Einfach,1,12,Backlog,"cmd/statuspage-devserver/main.go, systemd-Unit-Definition","`cmd/statuspage-devserver/main.go:48-58` setzt `Access-Control-Allow-Origin: *` ohne jede Authentifizierung. Der Dienst hat eine eigene systemd-Unit und kann damit trotz seines Namens dauerhaft laufen, nicht nur im Entwicklungskontext — ein offen erreichbarer, unauthentifizierter Endpunkt mit unbeschränktem CORS ist von jeder Webseite aus per Cross-Origin-Request auslesbar.",,,"Entweder: die systemd-Unit für `statuspage-devserver` wird aus produktiven Deployment-Artefakten entfernt bzw. so markiert, dass sie nur in Entwicklungsumgebungen installiert wird
Oder: der Dienst erhält eine Authentifizierung (z. B. Service-Token) und ein auf konkrete, vertrauenswürdige Origins eingeschränktes CORS statt `*`
Die gewählte Lösung ist im Code bzw. in der Deployment-Dokumentation nachvollziehbar begründet","Unauthentifizierten Cross-Origin-Request von einer beliebigen Origin gegen den Dienst stellen — vor dem Fix: Daten lesbar, nach dem Fix: entweder Dienst nicht produktiv erreichbar oder Request ohne gültige Auth/Origin abgelehnt
Falls Auth ergänzt: Zugriff mit gültigem Service-Token funktioniert weiterhin wie vorgesehen"
SEC-09,Vertrauenswürdige Client-IP-Ermittlung für Rate-Limiting statt erster X-Forwarded-For-Eintrag,🔒 Sicherheit,Backend,Mittel,2,12,Backlog,internal/lockout/iplimit.go,"`internal/lockout/iplimit.go:97-98` verwendet den ERSTEN Eintrag in `X-Forwarded-For` als Client-IP für das Rate-Limiting. Dieser Header ist vom Client frei setzbar — ein Angreifer kann pro Anfrage einen beliebigen neuen ""Absender"" behaupten und damit das IP-basierte Rate-Limit (z. B. gegen Brute-Force bei Login) beliebig oft umgehen.",,,"Die Client-IP für das Rate-Limiting wird aus einer vertrauenswürdigen Quelle ermittelt: entweder dem vom eigenen Reverse-Proxy gesetzten `X-Real-IP`, oder der letzten vertrauenswürdigen Adresse in der `X-Forwarded-For`-Kette (nicht der ersten, client-kontrollierten)
Ein Client, der beliebige zusätzliche Einträge vorne in `X-Forwarded-For` einfügt, kann das Rate-Limit nicht mehr durch Variation dieses Headers umgehen
Die Liste vertrauenswürdiger Proxy-Hops ist konfigurierbar (z. B. über Umgebungsvariable), nicht hartkodiert","Wiederholte Login-Fehlversuche von derselben realen Verbindung, jeweils mit unterschiedlichem, selbst gesetztem erstem `X-Forwarded-For`-Eintrag — vor dem Fix: Rate-Limit greift nicht, nach dem Fix: Rate-Limit greift nach der konfigurierten Anzahl Versuche
Normale Anfragen über den echten Reverse-Proxy mit korrekter Hop-Kette werden weiterhin korrekt nach tatsächlicher Client-IP limitiert"
1 Code Titel Epic Rolle Komplexitaet Aufwand_PT Welle Status Technik Beschreibung Haengt_ab_von Schaltet_frei Akzeptanzkriterien Pruefungen
2 TEN-01 Tenant-Registry & Datenbank-Provisioning 🏢 Mandantenverwaltung Backend Mittel 4 1 Fertig PostgreSQL, SQL-Migrationen, Go/pgx Schlanke Control-Plane-Registry-Datenbank (Tenant-Liste, Status, Verbindungsinformationen, Superadmin-Konten) plus automatisierte Provisioning-Routine, die für jeden neuen Mandanten eine eigene, vollständig isolierte PostgreSQL-Datenbank anlegt. API-01 AUD-01 CFG-01 IAM-01 LIC-01 LIC-02 TEN-02 TEN-04 TEN-06 TEN-07 TEN-09 Registry-Datenbank enthält Tenant-Metadaten und Verbindungsinformationen, aber keine Geschäftsdaten eines Mandanten Provisioning-Routine legt für einen neuen Mandanten eine vollständig isolierte Datenbank an und registriert sie transaktional in der Registry Zwei Mandanten-Datenbanken sind auf Infrastrukturebene komplett getrennt – ein Zugriff über eine falsch oder fehlend gefilterte Query in einer Tenant-Datenbank kann technisch keine Zeile eines anderen Mandanten zurückliefern, weil sie nicht in derselben Datenbank existiert Migration der Registry-Datenbank läuft gegen leere Datenbank durch und ist rückrollbar Provisioning-Test legt zwei Mandanten an, prüft dass beide Datenbanken physisch getrennt sind (unterschiedliche Datenbanknamen/-instanzen, kein gemeinsames Schema) Simulierter Anwendungs-Bug (Query ohne Tenant-Kontext) gegen eine Tenant-Datenbank liefert nachweislich niemals Daten eines anderen Mandanten, weil keine Verbindung dorthin besteht
3 TEN-02 Tenant-Onboarding-API & Self-Service 🏢 Mandantenverwaltung Backend Mittel 3 2 Fertig REST-API, Go/net/http, PostgreSQL Self-Service-API für die Anlage neuer Mandanten inklusive Validierung, Ersteinrichtung des Administrator-Kontos und Bestätigungs-Flow. TEN-01 TEN-03 TEN-05 Neuer Mandant lässt sich per API-Aufruf ohne manuellen Admin-Eingriff anlegen Erster Administrator-Account wird im selben Vorgang transaktional mit angelegt Doppelte Registrierung (gleicher Bezeichner/Domain) wird sauber abgewiesen Onboarding-Vorgang zweimal parallel ausgelöst, kein Doppel-Tenant entstanden Fehleingaben (leere Pflichtfelder, ungültige Domain) werden mit klaren Fehlermeldungen abgewiesen Erfolgreicher Durchlauf von einer zweiten Person end-to-end nachvollzogen
4 TEN-03 Tenant-Einstellungen & Branding 🏢 Mandantenverwaltung Backend Einfach 2 3 Fertig REST-API, PostgreSQL, JSON-Konfiguration Pro-Tenant-Einstellungen (Anzeigename, Logo, Farbschema, Zeitzone, Sprache) verwalten und über API lesbar/schreibbar machen. TEN-02 TEN-05 Einstellungen sind pro Tenant persistiert und über API abrufbar/änderbar Nicht gesetzte Werte fallen auf sinnvolle Systemvoreinstellungen zurück Änderungen an Einstellungen werden versioniert nachvollziehbar gespeichert Einstellungsänderung eines Tenants wirkt sich nicht auf einen anderen Tenant aus Fehlende Einstellungswerte liefern dokumentierte Defaults statt Fehler API-Schema von zweiter Person gegen die Dokumentation geprüft
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
+287 -12
View File
@@ -71,6 +71,13 @@
"emoji": "🎨",
"color": "#EC4899",
"description": "Gemeinsame Layout-Shell, Komponentenbibliothek und Design-Tokens für alle Modul-Frontends."
},
{
"key": "SEC",
"name": "Sicherheit",
"emoji": "🔒",
"color": "#DC2626",
"description": "Härtung von Auth, Session-Widerruf, Audit-Integrität und Rate-Limiting gegen konkrete Security-Review-Befunde."
}
],
"waves": [
@@ -204,6 +211,20 @@
"codes": [
"IAM-20"
]
},
{
"welle": 12,
"codes": [
"SEC-01",
"SEC-02",
"SEC-03",
"SEC-04",
"SEC-05",
"SEC-06",
"SEC-07",
"SEC-08",
"SEC-09"
]
}
],
"kritischerPfad": {
@@ -2963,7 +2984,7 @@
"Simuliert Lesezugriff auf Superadmin-Konten und prüft, dass ein Audit-Event mit korrektem Akteur/Zeitpunkt entsteht",
"Prüft Firewall-/pg_hba.conf-Konfiguration gegen Zugriff von außerhalb des Core-Dienstes"
],
"status": "Backlog",
"status": "Fertig",
"epicName": "Mandantenverwaltung",
"epicEmoji": "🏢",
"wave": 9,
@@ -2994,7 +3015,7 @@
"Prüft, dass jeder Tenant-KEK-Zugriff einen Audit-Event mit Akteur (Service-Credential), Zeitpunkt und betroffenem Tenant erzeugt",
"Prüft Alarmierung bei überschrittenem Rotationsintervall"
],
"status": "Backlog",
"status": "Fertig",
"epicName": "Core-API & Modulverwaltung",
"epicEmoji": "🔌",
"wave": 9,
@@ -3024,7 +3045,7 @@
"Massenwiderruf-Test: alle Sessions eines Benutzers widerrufen, prüfen dass alle zugehörigen Tokens abgelehnt werden, andere Benutzer unberührt bleiben",
"Lasttest der Widerrufsprüfung gegen die Auth-Middleware, um Performance-Regression auszuschließen"
],
"status": "Backlog",
"status": "Fertig",
"epicName": "Identität & Authentifizierung",
"epicEmoji": "👤",
"wave": 9,
@@ -3054,7 +3075,7 @@
"Negativtest: manipuliertes Token mit fremdem Signaturschlüssel wird abgelehnt",
"Negativtest: externer Claim behauptet Superadmin-Rolle, Login vergibt trotzdem höchstens die höchste zulässige tenant-scoped Rolle"
],
"status": "Backlog",
"status": "Fertig",
"epicName": "Identität & Authentifizierung",
"epicEmoji": "👤",
"wave": 9,
@@ -3083,7 +3104,7 @@
"Test gezielt mit einer absichtlich fehlerhaften Policy (Tenant-Admin erbt Superadmin) ausführen, muss fehlschlagen",
"Test gegen die aktuelle, korrekte Policy ausführen, muss erfolgreich durchlaufen"
],
"status": "Backlog",
"status": "Fertig",
"epicName": "Rollen & Rechte",
"epicEmoji": "🔐",
"wave": 9,
@@ -3113,7 +3134,7 @@
"Prüft, dass der Ablehnungsversuch selbst sichtbar protokolliert wird",
"Regressionstest: normales Schreiben/Lesen von Audit-Einträgen funktioniert weiterhin"
],
"status": "Backlog",
"status": "Fertig",
"epicName": "Audit & Compliance",
"epicEmoji": "📜",
"wave": 9,
@@ -3143,7 +3164,7 @@
"Test mit nachträglich manipulierter Migrationsdatei (Prüfsumme weicht ab): Rollout bricht vor der ersten Tenant-Datenbank ab",
"Prüft, dass der Abbruch einen nachvollziehbaren Audit-Event erzeugt (wer/was/wann)"
],
"status": "Backlog",
"status": "Fertig",
"epicName": "Mandantenverwaltung",
"epicEmoji": "🏢",
"wave": 10,
@@ -3173,7 +3194,7 @@
"Test der LRU-Verdrängung bei Erreichen der Pool-Obergrenze",
"Regressionstest: normaler Betrieb mit wenigen Mandanten bleibt unverändert performant"
],
"status": "Backlog",
"status": "Fertig",
"epicName": "Mandantenverwaltung",
"epicEmoji": "🏢",
"wave": 10,
@@ -3204,7 +3225,7 @@
"Regressionstest: LDAP-Login/Synchronisation funktioniert nach Umstellung auf verschlüsselte Ablage weiterhin korrekt",
"Test: Entschlüsselung schlägt fehl, wenn der falsche Tenant-KEK verwendet wird (kein Cross-Tenant-Zugriff auf Bind-Credentials)"
],
"status": "Backlog",
"status": "Fertig",
"epicName": "Identität & Authentifizierung",
"epicEmoji": "👤",
"wave": 10,
@@ -3235,7 +3256,7 @@
"Test: Massenänderung an Feature-Flags gleichzeitig mit vielen Anfragen — keine falsch-positive Freigabe während der Invalidierung",
"Regressionstest: normaler Betrieb mit erreichbarer Registry bleibt unverändert"
],
"status": "Backlog",
"status": "Fertig",
"epicName": "Lizenzierung & Feature Flags",
"epicEmoji": "🎫",
"wave": 10,
@@ -3265,7 +3286,7 @@
"Simulierter Mehr-Instanz-Betrieb mit aktivem mTLS: keine Warnung",
"Prüft, dass die Betriebs-Checkliste den mTLS-Schritt beim Stufenwechsel enthält"
],
"status": "Backlog",
"status": "Fertig",
"epicName": "Monitoring & Betrieb",
"epicEmoji": "📈",
"wave": 10,
@@ -3296,12 +3317,266 @@
"Test: künstlich stark erhöhte Zugriffsrate desselben Tokens erzeugt den erwarteten Audit-Event",
"Regressionstest: normale, unauffällige Token-Nutzung erzeugt keine Falsch-Alarme im regulären Betrieb"
],
"status": "Backlog",
"status": "Fertig",
"epicName": "Identität & Authentifizierung",
"epicEmoji": "👤",
"wave": 11,
"unlocks": [],
"prompt": "# IAM-20 – Anomalie-Erkennung bei API-Token-/Service-Account-Nutzung\n\n**Epic:** 👤 Identität & Authentifizierung | **Rolle:** Backend | **Komplexität:** Mittel | **Aufwand:** 2 Personentage | **Welle:** 11\n\n---\n\n## Rolle\nDu bist erfahrene Entwicklerin im Bereich Backend und baust NEXARCH Core (Identity, Tenancy, Auth, Lizenzierung, Feature Flags, Audit, zentrale Konfiguration, API-Grundgerüst, Monitoring), das sich an Zitadel, Casbin, Unleash und Prometheus/Grafana orientiert und deren bekannte Schwächen – u. a. von Keycloak – bewusst vermeidet.\nServerseitige Logik, Datenzugriff und Schnittstelle. Keine Geschäftslogik in der Oberfläche.\n\n## Auftrag\nIAM-09 deckt Scopes und Ablaufzeit für API-Token/Service-Accounts ab, aber ein gestohlenes, noch gültiges Token bleibt bis zum Ablauf unauffällig nutzbar. Einfache Anomalie-Erkennung ergänzen: Nutzung eines Tokens aus einer bisher nicht gesehenen IP-Range oder mit einer gegenüber dem bisherigen Muster stark abweichenden Zugriffsrate erzeugt einen Audit-Event mit erhöhtem Schweregrad (Kopplung an AUD-01) und optional eine Benachrichtigung an den Token-Besitzer/Tenant-Admin über den Core-Dispatcher (CFG-02). Keine automatische Sperre – die Erkennung ist ein Hinweissignal, keine eigenständige Durchsetzung, um Fehlalarme nicht zu blockierenden Vorfällen zu machen.\n\n**Technische Grundlage:** PostgreSQL, IP-/Rate-Heuristik, Core-Audit-Kopplung (AUD-01)\n\n## Produkt-DNA für diesen Bereich\n**Übernehmen:**\n- Zitadel: sichtbare Session-/Token-Aktivität statt reiner Gültigkeitsprüfung ohne Kontext.\n\n**Bewusst vermeiden:**\n- Keycloak – Admin-Events erfassen Login-Anomalien nicht standardmäßig, nur bei manueller Zusatzkonfiguration.: Anomalie-Erkennung ist von Anfang an aktiver Bestandteil der Token-Nutzung, keine optionale Zusatzkomponente.\n\n## Ausgangslage\nSetzt auf folgenden bereits fertigen Kacheln auf: IAM-09, AUD-01.\n\n## Akzeptanzkriterien\nDie Kachel gilt erst als umgesetzt, wenn alle Punkte erfüllt sind:\n1. Nutzung eines API-Tokens/Service-Accounts aus einer bisher nicht beobachteten IP-Range erzeugt einen Audit-Event mit erhöhtem Schweregrad\n2. Ein gegenüber dem historischen Muster stark abweichender Zugriffsratenanstieg desselben Tokens erzeugt ebenfalls einen Audit-Event\n3. Das Token bleibt bei erkannter Anomalie weiterhin gültig (keine automatische Sperre) – die Erkennung dient als Hinweissignal, tatsächliche Sperre erfolgt manuell oder über einen bewussten separaten Prozess\n\n## Prüfungen vor der Abnahme\nFühre jede Prüfung tatsächlich durch und halte das Ergebnis schriftlich fest. Nicht durchgeführte Prüfungen zählen als offen:\n1. Test: Zugriff mit einem Token aus einer neuen IP-Range erzeugt den erwarteten Audit-Event\n2. Test: künstlich stark erhöhte Zugriffsrate desselben Tokens erzeugt den erwarteten Audit-Event\n3. Regressionstest: normale, unauffällige Token-Nutzung erzeugt keine Falsch-Alarme im regulären Betrieb\n\n## Bekannte Fehler vermeiden\n\nThreat-Model-Finding (Core-Board, 2026-09-12, Finding #2): ohne Anomalie-Erkennung bleibt ein gestohlenes, noch gültiges API-Token bis zum regulären Ablauf unauffällig nutzbar – reine Scope-/Ablaufzeit-Prüfung aus IAM-09 erkennt einen Missbrauch aus fremdem Kontext nicht.\n\n## Nicht Bestandteil dieser Kachel\nDiese Punkte gehören ausdrücklich in andere Kacheln. Bereite höchstens die Schnittstelle dafür vor:\n- `IAM-09` API-Token & Service-Accounts\n- `AUD-01` Zentrales Audit-Log-Modell\n\n## Arbeitsweise\n1. Lies zuerst den bestehenden Code der Vorbedingungen und halte dich an die dort etablierten Muster.\n2. Setze die kleinste Lösung um, die alle Akzeptanzkriterien erfüllt. Kein Umbau angrenzender Bereiche.\n3. Schreibe zu jedem Akzeptanzkriterium mindestens einen automatisierten Test.\n4. Führe alle Prüfungen aus und dokumentiere Zielwert und Ergebnis.\n5. Deutschsprachige Oberflächentexte, englischsprachige Bezeichner im Code.\n6. Keine Zugangsdaten, Schlüssel oder Verbindungszeichenfolgen im Code – ausschließlich über Umgebungsvariablen.\n\n## Abschluss\n- Commit-Nachricht: `IAM-20: anomalie-erkennung-bei-api-token-service-account-nutzung`\n- Branch: `feature/iam-20-anomalie-erkennung-bei-api-token-service-account-nutzung`\n- Schrittweise committen, Branch pushen, dann anhalten. Kein Merge, kein Deploy.\n- In der Beschreibung des Pull Request die durchgeführten Prüfungen mit Ergebnis auflisten.\n"
},
{
"code": "SEC-01",
"title": "Verbindliche Session-Revocation-Pruefung auf allen RequireAuth-Routen",
"epic": "SEC",
"featureIds": [],
"role": "Backend",
"complexity": "Mittel",
"estimateDays": 2,
"tech": "internal/auth/middleware.go, internal/auth/token.go, cmd/account-api, cmd/rbac-admin-api, cmd/notifyprefs-api",
"description": "In `internal/auth/middleware.go:26,40` wird der Revocation-Status eines Tokens nur geprüft, wenn der aufrufende Code explizit einen `revocationStore`/Checker übergibt — Standardverhalten ist \"nicht prüfen\". In `cmd/account-api/main.go:212-217` registrieren `/account/me`, `/account/change-password` und `/auth/totp/*` `RequireAuth` ohne diesen Checker (nur `moduletoken.RegisterToken` in Zeile 203 übergibt ihn). `cmd/rbac-admin-api/main.go:73-80` und `cmd/notifyprefs-api/main.go:71-73` haben dieselbe Lücke. Zusammen mit der 90-Tage-Token-Laufzeit (`internal/auth/token.go:221`, `MobileAccessTokenTTL`) bedeutet das: ein gestohlenes Token bleibt nach Logout UND nach einem Passwortwechsel (der `RevokeAllForUser` auslöst) bis zu 90 Tage lang gültig für Profilzugriff, TOTP-Setup und die komplette RBAC-Admin-API.",
"dependsOn": [],
"akzeptanz": [
"`RequireAuth` prüft den Revocation-Status immer, wenn ein Revocation-Store im Prozess konfiguriert ist — kein optionaler Parameter mit Default \"nicht prüfen\" mehr; fehlt ein Store, scheitert der Prozessstart statt den Check stillschweigend zu überspringen",
"`cmd/account-api/main.go` übergibt an allen `RequireAuth`-Aufrufen (`/account/me`, `/account/change-password`, `/auth/totp/*`) denselben Revocation-Store wie `moduletoken.RegisterToken`",
"`cmd/rbac-admin-api/main.go` und `cmd/notifyprefs-api/main.go` übergeben ebenfalls den Revocation-Store an jeden `RequireAuth`-Aufruf",
"Ein nach Logout oder Passwortwechsel widerrufenes Token wird auf jeder dieser Routen mit 401 abgelehnt, nicht erst nach Ablauf der 90-Tage-TTL"
],
"pruefungen": [
"Token ausstellen, Logout auslösen, danach `/account/me` mit demselben Token aufrufen — vor dem Fix: 200, nach dem Fix: 401, Nachweis per Request/Response-Log",
"Token ausstellen, Passwortwechsel auslösen (`RevokeAllForUser`), danach `/auth/totp/setup/begin` und einen RBAC-Admin-Endpunkt mit dem alten Token aufrufen — beide müssen 401 liefern",
"Architekturtest (analog API-21/INT-15-Muster), der einen `RequireAuth`-Aufruf ohne Revocation-Store im Build scheitern lässt, mit absichtlich eingefügtem Verstoß verifiziert und danach entfernt"
],
"status": "Backlog",
"epicName": "Sicherheit",
"epicEmoji": "🔒",
"wave": 12,
"unlocks": [],
"prompt": "# SEC-01 – Verbindliche Session-Revocation-Pruefung auf allen RequireAuth-Routen\n\n**Epic:** 🔒 Sicherheit | **Rolle:** Backend | **Komplexität:** Mittel | **Aufwand:** 2 Personentag(e) | **Welle:** 12\n\n---\n\n## Rolle\nDu bist erfahrene Entwicklerin im Bereich Backend und baust NEXARCH Core (Identity, Tenancy, Auth, Lizenzierung, Feature Flags, Audit, zentrale Konfiguration, API-Grundgerüst, Monitoring), das sich an Zitadel, Casbin, Unleash und Prometheus/Grafana orientiert und deren bekannte Schwächen bewusst vermeidet.\nServerseitige Logik, Datenzugriff und Schnittstelle. Keine Geschäftslogik in der Oberfläche.\n\n## Auftrag\nIn `internal/auth/middleware.go:26,40` wird der Revocation-Status eines Tokens nur geprüft, wenn der aufrufende Code explizit einen `revocationStore`/Checker übergibt — Standardverhalten ist \"nicht prüfen\". In `cmd/account-api/main.go:212-217` registrieren `/account/me`, `/account/change-password` und `/auth/totp/*` `RequireAuth` ohne diesen Checker (nur `moduletoken.RegisterToken` in Zeile 203 übergibt ihn). `cmd/rbac-admin-api/main.go:73-80` und `cmd/notifyprefs-api/main.go:71-73` haben dieselbe Lücke. Zusammen mit der 90-Tage-Token-Laufzeit (`internal/auth/token.go:221`, `MobileAccessTokenTTL`) bedeutet das: ein gestohlenes Token bleibt nach Logout UND nach einem Passwortwechsel (der `RevokeAllForUser` auslöst) bis zu 90 Tage lang gültig für Profilzugriff, TOTP-Setup und die komplette RBAC-Admin-API.\n\n**Technische Grundlage:** internal/auth/middleware.go, internal/auth/token.go, cmd/account-api, cmd/rbac-admin-api, cmd/notifyprefs-api\n\n## Produkt-DNA für diesen Bereich\n**Übernehmen:**\n- Zitadel: Session-Widerruf wirkt sofort und lückenlos auf allen Routen, nicht nur ausgewählten.\n\n**Bewusst vermeiden:**\n- Keycloak – historisch inkonsistente Durchsetzung von Token-Revocation über verschiedene Endpunkte hinweg.\n\n\n## Ausgangslage\n\n## Akzeptanzkriterien\nDie Kachel gilt erst als umgesetzt, wenn alle Punkte erfüllt sind:\n1. `RequireAuth` prüft den Revocation-Status immer, wenn ein Revocation-Store im Prozess konfiguriert ist — kein optionaler Parameter mit Default \"nicht prüfen\" mehr; fehlt ein Store, scheitert der Prozessstart statt den Check stillschweigend zu überspringen\n2. `cmd/account-api/main.go` übergibt an allen `RequireAuth`-Aufrufen (`/account/me`, `/account/change-password`, `/auth/totp/*`) denselben Revocation-Store wie `moduletoken.RegisterToken`\n3. `cmd/rbac-admin-api/main.go` und `cmd/notifyprefs-api/main.go` übergeben ebenfalls den Revocation-Store an jeden `RequireAuth`-Aufruf\n4. Ein nach Logout oder Passwortwechsel widerrufenes Token wird auf jeder dieser Routen mit 401 abgelehnt, nicht erst nach Ablauf der 90-Tage-TTL\n\n## Prüfungen vor der Abnahme\nFühre jede Prüfung tatsächlich durch und halte das Ergebnis schriftlich fest. Nicht durchgeführte Prüfungen zählen als offen:\n1. Token ausstellen, Logout auslösen, danach `/account/me` mit demselben Token aufrufen — vor dem Fix: 200, nach dem Fix: 401, Nachweis per Request/Response-Log\n2. Token ausstellen, Passwortwechsel auslösen (`RevokeAllForUser`), danach `/auth/totp/setup/begin` und einen RBAC-Admin-Endpunkt mit dem alten Token aufrufen — beide müssen 401 liefern\n3. Architekturtest (analog API-21/INT-15-Muster), der einen `RequireAuth`-Aufruf ohne Revocation-Store im Build scheitern lässt, mit absichtlich eingefügtem Verstoß verifiziert und danach entfernt\n\n## Bekannte Fehler vermeiden\n\nSecurity-Review (main-Branch, Fable 5.1, SEC): siehe Auftrag oben für Datei:Zeile-Belege. Ohne Fix bleibt der beschriebene Angriff möglich.\n\n## Nicht Bestandteil dieser Kachel\nDiese Punkte gehören ausdrücklich in andere Kacheln. Bereite höchstens die Schnittstelle dafür vor:\n- Verkürzung der 90-Tage-Mobile-Token-TTL selbst (separates Abwägungsthema Usability vs. Risiko, eigene Kachel falls gewünscht)\n- 2FA-Downgrade-Problem aus demselben Befund (siehe SEC-02)\n\n## Arbeitsweise\n1. Lies zuerst den bestehenden Code der Vorbedingungen und halte dich an die dort etablierten Muster.\n2. Setze die kleinste Lösung um, die alle Akzeptanzkriterien erfüllt. Kein Umbau angrenzender Bereiche.\n3. Schreibe zu jedem Akzeptanzkriterium mindestens einen automatisierten Test.\n4. Führe alle Prüfungen aus und dokumentiere Zielwert und Ergebnis.\n5. Deutschsprachige Oberflächentexte, englischsprachige Bezeichner im Code.\n6. Keine Zugangsdaten, Schlüssel oder Verbindungszeichenfolgen im Code – ausschließlich über Umgebungsvariablen.\n\n## Abschluss\n- Commit-Nachricht: `SEC-01: verbindliche session-revocation-pruefung auf allen requireauth-routen`\n- Branch: `feature/sec-01-verbindliche-session-revocation-pruefung-auf-allen-requireau`\n- Schrittweise committen, Branch pushen, dann anhalten. Kein Merge, kein Deploy.\n- In der Beschreibung des Pull Request die durchgeführten Prüfungen mit Ergebnis auflisten.\n"
},
{
"code": "SEC-02",
"title": "Re-Authentifizierung vor TOTP-Setup/-Reset erzwingen",
"epic": "SEC",
"featureIds": [],
"role": "Backend",
"complexity": "Mittel",
"estimateDays": 2,
"tech": "internal/totp/handler.go, internal/totp/store.go",
"description": "`internal/totp/store.go:99-102` setzt per `ON CONFLICT (user_id) DO UPDATE SET secret = NULL, secret_ciphertext=$2, confirmed=false` die 2FA eines Nutzers zurück. `SetupBegin` in `internal/totp/handler.go` (ca. Zeile 164-185) verlangt dafür weder das aktuelle Passwort noch einen gültigen TOTP-Code — die Route liegt nur hinter `RequireAuth`. Damit kann `POST /auth/totp/setup/begin` mit einer gestohlenen Session (z. B. XSS oder Session-Cookie-Diebstahl) sofort `IsEnabled=false` setzen und die 2FA des Opfers deaktivieren, ohne dass bestehende Recovery-Codes invalidiert werden.",
"dependsOn": [],
"akzeptanz": [
"`SetupBegin` (und jede andere Route, die `confirmed=false`/`secret=NULL` setzen kann) verlangt zusätzlich zur gültigen Session eine frische Re-Authentifizierung: aktuelles Passwort ODER einen gültigen bestehenden TOTP-Code",
"Fehlt die Re-Authentifizierung oder ist sie falsch, bleibt der bestehende TOTP-Zustand (inklusive Recovery-Codes) unverändert und die Anfrage wird mit 401/403 abgelehnt",
"Ein erfolgreicher TOTP-Reset invalidiert die bisherigen Recovery-Codes explizit, statt sie unangetastet weiterbestehen zu lassen"
],
"pruefungen": [
"`POST /auth/totp/setup/begin` nur mit gültiger Session und ohne Passwort/TOTP-Code aufrufen — muss vor dem Fix durchgehen (2FA wird deaktiviert) und nach dem Fix mit 401/403 scheitern, 2FA bleibt aktiv",
"Gleicher Aufruf mit korrektem aktuellem Passwort — muss erfolgreich das Setup beginnen",
"Nach erfolgreichem Reset: alte Recovery-Codes gegen den Recovery-Login-Endpunkt testen — müssen abgelehnt werden"
],
"status": "Backlog",
"epicName": "Sicherheit",
"epicEmoji": "🔒",
"wave": 12,
"unlocks": [],
"prompt": "# SEC-02 – Re-Authentifizierung vor TOTP-Setup/-Reset erzwingen\n\n**Epic:** 🔒 Sicherheit | **Rolle:** Backend | **Komplexität:** Mittel | **Aufwand:** 2 Personentag(e) | **Welle:** 12\n\n---\n\n## Rolle\nDu bist erfahrene Entwicklerin im Bereich Backend und baust NEXARCH Core (Identity, Tenancy, Auth, Lizenzierung, Feature Flags, Audit, zentrale Konfiguration, API-Grundgerüst, Monitoring), das sich an Zitadel, Casbin, Unleash und Prometheus/Grafana orientiert und deren bekannte Schwächen bewusst vermeidet.\nServerseitige Logik, Datenzugriff und Schnittstelle. Keine Geschäftslogik in der Oberfläche.\n\n## Auftrag\n`internal/totp/store.go:99-102` setzt per `ON CONFLICT (user_id) DO UPDATE SET secret = NULL, secret_ciphertext=$2, confirmed=false` die 2FA eines Nutzers zurück. `SetupBegin` in `internal/totp/handler.go` (ca. Zeile 164-185) verlangt dafür weder das aktuelle Passwort noch einen gültigen TOTP-Code — die Route liegt nur hinter `RequireAuth`. Damit kann `POST /auth/totp/setup/begin` mit einer gestohlenen Session (z. B. XSS oder Session-Cookie-Diebstahl) sofort `IsEnabled=false` setzen und die 2FA des Opfers deaktivieren, ohne dass bestehende Recovery-Codes invalidiert werden.\n\n**Technische Grundlage:** internal/totp/handler.go, internal/totp/store.go\n\n## Produkt-DNA für diesen Bereich\n**Übernehmen:**\n- Zitadel: sicherheitskritische Selbstverwaltungsaktionen (2FA-Änderung) verlangen Step-up-Authentifizierung.\n\n**Bewusst vermeiden:**\n- Keine 2FA-Verwaltung allein auf Basis einer bestehenden Session ohne Re-Auth.\n\n\n## Ausgangslage\n\n## Akzeptanzkriterien\nDie Kachel gilt erst als umgesetzt, wenn alle Punkte erfüllt sind:\n1. `SetupBegin` (und jede andere Route, die `confirmed=false`/`secret=NULL` setzen kann) verlangt zusätzlich zur gültigen Session eine frische Re-Authentifizierung: aktuelles Passwort ODER einen gültigen bestehenden TOTP-Code\n2. Fehlt die Re-Authentifizierung oder ist sie falsch, bleibt der bestehende TOTP-Zustand (inklusive Recovery-Codes) unverändert und die Anfrage wird mit 401/403 abgelehnt\n3. Ein erfolgreicher TOTP-Reset invalidiert die bisherigen Recovery-Codes explizit, statt sie unangetastet weiterbestehen zu lassen\n\n## Prüfungen vor der Abnahme\nFühre jede Prüfung tatsächlich durch und halte das Ergebnis schriftlich fest. Nicht durchgeführte Prüfungen zählen als offen:\n1. `POST /auth/totp/setup/begin` nur mit gültiger Session und ohne Passwort/TOTP-Code aufrufen — muss vor dem Fix durchgehen (2FA wird deaktiviert) und nach dem Fix mit 401/403 scheitern, 2FA bleibt aktiv\n2. Gleicher Aufruf mit korrektem aktuellem Passwort — muss erfolgreich das Setup beginnen\n3. Nach erfolgreichem Reset: alte Recovery-Codes gegen den Recovery-Login-Endpunkt testen — müssen abgelehnt werden\n\n## Bekannte Fehler vermeiden\n\nSecurity-Review (main-Branch, Fable 5.1, SEC): siehe Auftrag oben für Datei:Zeile-Belege. Ohne Fix bleibt der beschriebene Angriff möglich.\n\n## Nicht Bestandteil dieser Kachel\nDiese Punkte gehören ausdrücklich in andere Kacheln. Bereite höchstens die Schnittstelle dafür vor:\n- Revocation-Check auf `RequireAuth` selbst (siehe SEC-01, ist Voraussetzung aber eigene Kachel)\n\n## Arbeitsweise\n1. Lies zuerst den bestehenden Code der Vorbedingungen und halte dich an die dort etablierten Muster.\n2. Setze die kleinste Lösung um, die alle Akzeptanzkriterien erfüllt. Kein Umbau angrenzender Bereiche.\n3. Schreibe zu jedem Akzeptanzkriterium mindestens einen automatisierten Test.\n4. Führe alle Prüfungen aus und dokumentiere Zielwert und Ergebnis.\n5. Deutschsprachige Oberflächentexte, englischsprachige Bezeichner im Code.\n6. Keine Zugangsdaten, Schlüssel oder Verbindungszeichenfolgen im Code – ausschließlich über Umgebungsvariablen.\n\n## Abschluss\n- Commit-Nachricht: `SEC-02: re-authentifizierung vor totp-setup/-reset erzwingen`\n- Branch: `feature/sec-02-re-authentifizierung-vor-totp-setup/-reset-erzwingen`\n- Schrittweise committen, Branch pushen, dann anhalten. Kein Merge, kein Deploy.\n- In der Beschreibung des Pull Request die durchgeführten Prüfungen mit Ergebnis auflisten.\n"
},
{
"code": "SEC-03",
"title": "Modul-Tenant-Bindung und Delta-Validierung in resync-api",
"epic": "SEC",
"featureIds": [],
"role": "Backend",
"complexity": "Mittel",
"estimateDays": 2,
"tech": "internal/resync/handler.go, internal/kek/handler.go (Referenzmuster)",
"description": "`internal/resync/handler.go:52-60` verwirft den von `Authenticate` zurückgegebenen Modulnamen, statt ihn weiterzuverwenden. Zeile 90 ruft `h.audit.Record(ctx, e.TenantSlug, e.Actor, e.Action, e.Target, e.Metadata, e.OccurredAt)` mit `TenantSlug` direkt aus dem Request-Body auf; Zeile 120 ruft `usage.Increment(ctx, d.TenantSlug, d.Metric, d.Delta)` mit einem beliebigen `int64 Delta` auf, das auch negativ sein kann. Ein `IsActive(tenant, module)`-Check wie in `internal/kek/handler.go:853-869` fehlt komplett. Damit kann ein kompromittiertes Modul-Credential rückdatierte, nicht löschbare Audit-Einträge für fremde Mandanten erzeugen oder `storage_bytes` per negativem Delta künstlich auf 0 setzen (Quota-Umgehung).",
"dependsOn": [
"API-13"
],
"akzeptanz": [
"Vor jedem `audit.Record`- und `usage.Increment`-Aufruf prüft der Handler per `IsActive(tenant, module)` (analog kek-Muster), dass das authentifizierte Modul-Credential für genau diesen `TenantSlug` aktiv zugelassen ist",
"`TenantSlug` aus dem Request-Body wird gegen die tatsächliche Modul-Tenant-Zuordnung des Credentials validiert, nicht blind übernommen — Abweichung führt zu Ablehnung (4xx), nicht zu stillschweigender Korrektur",
"`Delta` in Usage-Increment-Requests wird auf einen sinnvollen Wertebereich begrenzt (kein beliebig negativer Wert, der eine Quota auf 0 oder negativ setzen kann); unzulässige Werte werden abgelehnt"
],
"pruefungen": [
"Request mit gültigem Modul-Credential für Mandant A, aber `TenantSlug` von Mandant B im Body — vor dem Fix: Audit-Eintrag für B wird geschrieben, nach dem Fix: Ablehnung, kein Eintrag für B",
"Usage-Increment mit stark negativem `Delta` gegen eine bestehende Quota von Mandant A — vor dem Fix: Quota wird auf 0 oder negativ gesetzt, nach dem Fix: Ablehnung oder Begrenzung auf plausiblen Bereich",
"Regulärer Resync-Vorgang eines zugelassenen Moduls für den eigenen Mandanten bleibt nach dem Fix unverändert funktionsfähig"
],
"status": "Backlog",
"epicName": "Sicherheit",
"epicEmoji": "🔒",
"wave": 12,
"unlocks": [],
"prompt": "# SEC-03 – Modul-Tenant-Bindung und Delta-Validierung in resync-api\n\n**Epic:** 🔒 Sicherheit | **Rolle:** Backend | **Komplexität:** Mittel | **Aufwand:** 2 Personentag(e) | **Welle:** 12\n\n---\n\n## Rolle\nDu bist erfahrene Entwicklerin im Bereich Backend und baust NEXARCH Core (Identity, Tenancy, Auth, Lizenzierung, Feature Flags, Audit, zentrale Konfiguration, API-Grundgerüst, Monitoring), das sich an Zitadel, Casbin, Unleash und Prometheus/Grafana orientiert und deren bekannte Schwächen bewusst vermeidet.\nServerseitige Logik, Datenzugriff und Schnittstelle. Keine Geschäftslogik in der Oberfläche.\n\n## Auftrag\n`internal/resync/handler.go:52-60` verwirft den von `Authenticate` zurückgegebenen Modulnamen, statt ihn weiterzuverwenden. Zeile 90 ruft `h.audit.Record(ctx, e.TenantSlug, e.Actor, e.Action, e.Target, e.Metadata, e.OccurredAt)` mit `TenantSlug` direkt aus dem Request-Body auf; Zeile 120 ruft `usage.Increment(ctx, d.TenantSlug, d.Metric, d.Delta)` mit einem beliebigen `int64 Delta` auf, das auch negativ sein kann. Ein `IsActive(tenant, module)`-Check wie in `internal/kek/handler.go:853-869` fehlt komplett. Damit kann ein kompromittiertes Modul-Credential rückdatierte, nicht löschbare Audit-Einträge für fremde Mandanten erzeugen oder `storage_bytes` per negativem Delta künstlich auf 0 setzen (Quota-Umgehung).\n\n**Technische Grundlage:** internal/resync/handler.go, internal/kek/handler.go (Referenzmuster)\n\n## Produkt-DNA für diesen Bereich\n**Übernehmen:**\n- Casbin/Zitadel-Muster: jede privilegierte Schreiboperation prüft die tatsächliche Berechtigung des Akteurs, nie nur den Request-Inhalt.\n\n**Bewusst vermeiden:**\n- Vertrauen auf Client-/Request-geliefertes `TenantSlug` ohne serverseitige Bindungsprüfung.\n\n\n## Ausgangslage\nSetzt auf folgenden bereits fertigen Kacheln auf: API-13.\n\n## Akzeptanzkriterien\nDie Kachel gilt erst als umgesetzt, wenn alle Punkte erfüllt sind:\n1. Vor jedem `audit.Record`- und `usage.Increment`-Aufruf prüft der Handler per `IsActive(tenant, module)` (analog kek-Muster), dass das authentifizierte Modul-Credential für genau diesen `TenantSlug` aktiv zugelassen ist\n2. `TenantSlug` aus dem Request-Body wird gegen die tatsächliche Modul-Tenant-Zuordnung des Credentials validiert, nicht blind übernommen — Abweichung führt zu Ablehnung (4xx), nicht zu stillschweigender Korrektur\n3. `Delta` in Usage-Increment-Requests wird auf einen sinnvollen Wertebereich begrenzt (kein beliebig negativer Wert, der eine Quota auf 0 oder negativ setzen kann); unzulässige Werte werden abgelehnt\n\n## Prüfungen vor der Abnahme\nFühre jede Prüfung tatsächlich durch und halte das Ergebnis schriftlich fest. Nicht durchgeführte Prüfungen zählen als offen:\n1. Request mit gültigem Modul-Credential für Mandant A, aber `TenantSlug` von Mandant B im Body — vor dem Fix: Audit-Eintrag für B wird geschrieben, nach dem Fix: Ablehnung, kein Eintrag für B\n2. Usage-Increment mit stark negativem `Delta` gegen eine bestehende Quota von Mandant A — vor dem Fix: Quota wird auf 0 oder negativ gesetzt, nach dem Fix: Ablehnung oder Begrenzung auf plausiblen Bereich\n3. Regulärer Resync-Vorgang eines zugelassenen Moduls für den eigenen Mandanten bleibt nach dem Fix unverändert funktionsfähig\n\n## Bekannte Fehler vermeiden\n\nSecurity-Review (main-Branch, Fable 5.1, SEC): siehe Auftrag oben für Datei:Zeile-Belege. Ohne Fix bleibt der beschriebene Angriff möglich.\n\n## Nicht Bestandteil dieser Kachel\nDiese Punkte gehören ausdrücklich in andere Kacheln. Bereite höchstens die Schnittstelle dafür vor:\n- Generelle Überarbeitung des resync-Protokolls jenseits der Tenant-Bindung und Delta-Validierung\n\n## Arbeitsweise\n1. Lies zuerst den bestehenden Code der Vorbedingungen und halte dich an die dort etablierten Muster.\n2. Setze die kleinste Lösung um, die alle Akzeptanzkriterien erfüllt. Kein Umbau angrenzender Bereiche.\n3. Schreibe zu jedem Akzeptanzkriterium mindestens einen automatisierten Test.\n4. Führe alle Prüfungen aus und dokumentiere Zielwert und Ergebnis.\n5. Deutschsprachige Oberflächentexte, englischsprachige Bezeichner im Code.\n6. Keine Zugangsdaten, Schlüssel oder Verbindungszeichenfolgen im Code – ausschließlich über Umgebungsvariablen.\n\n## Abschluss\n- Commit-Nachricht: `SEC-03: modul-tenant-bindung und delta-validierung in resync-api`\n- Branch: `feature/sec-03-modul-tenant-bindung-und-delta-validierung-in-resync-api`\n- Schrittweise committen, Branch pushen, dann anhalten. Kein Merge, kein Deploy.\n- In der Beschreibung des Pull Request die durchgeführten Prüfungen mit Ergebnis auflisten.\n"
},
{
"code": "SEC-04",
"title": "Passwort-Policy-Durchsetzung und Session-Widerruf nach Reset",
"epic": "SEC",
"featureIds": [],
"role": "Backend",
"complexity": "Mittel",
"estimateDays": 2,
"tech": "internal/pwpolicy/policy.go, internal/authtoken/flow.go, internal/auth/changepassword.go",
"description": "`internal/pwpolicy/policy.go:36` definiert `Validate`, hat aber laut Grep null Aufrufer im gesamten Repository — die Passwort-Policy wird nirgends angewendet. `internal/authtoken/flow.go:31-36` und `internal/auth/changepassword.go:79` übernehmen ein neues Passwort ohne vorherigen Policy-Check. Zusätzlich wird nach einem Passwort-Reset `RevokeAllForUser` nicht aufgerufen — bestehende Sessions und Tokens bleiben nach einem Reset gültig (verschärft durch die 90-Tage-TTL aus SEC-01).",
"dependsOn": [
"SEC-01"
],
"akzeptanz": [
"Jede Stelle, die ein neues Klartext-Passwort übernimmt (Setup, Change, Reset), ruft vorher `pwpolicy.Validate` auf; ein die Policy verletzendes Passwort wird mit klarer Fehlermeldung abgelehnt",
"Nach erfolgreichem Passwort-Reset (`authtoken`-Flow) ruft der Handler `RevokeAllForUser` für den betroffenen Nutzer auf",
"Nach erfolgreichem Passwort-Change (`changepassword.go`) gilt dieselbe Widerrufspflicht bereits (Bestandscode prüfen) oder wird ergänzt, falls dort ebenfalls eine Lücke besteht"
],
"pruefungen": [
"Passwort-Setup/-Change/-Reset mit einem Policy-verletzenden Passwort (z. B. zu kurz) — muss vor dem Fix durchgehen und nach dem Fix abgelehnt werden",
"Vor Reset ein Token/Session ausstellen, Reset durchführen, danach das alte Token gegen eine geschützte Route prüfen — muss nach dem Fix 401 liefern",
"Gültiges, policy-konformes Passwort wird nach dem Fix weiterhin erfolgreich übernommen"
],
"status": "Backlog",
"epicName": "Sicherheit",
"epicEmoji": "🔒",
"wave": 12,
"unlocks": [],
"prompt": "# SEC-04 – Passwort-Policy-Durchsetzung und Session-Widerruf nach Reset\n\n**Epic:** 🔒 Sicherheit | **Rolle:** Backend | **Komplexität:** Mittel | **Aufwand:** 2 Personentag(e) | **Welle:** 12\n\n---\n\n## Rolle\nDu bist erfahrene Entwicklerin im Bereich Backend und baust NEXARCH Core (Identity, Tenancy, Auth, Lizenzierung, Feature Flags, Audit, zentrale Konfiguration, API-Grundgerüst, Monitoring), das sich an Zitadel, Casbin, Unleash und Prometheus/Grafana orientiert und deren bekannte Schwächen bewusst vermeidet.\nServerseitige Logik, Datenzugriff und Schnittstelle. Keine Geschäftslogik in der Oberfläche.\n\n## Auftrag\n`internal/pwpolicy/policy.go:36` definiert `Validate`, hat aber laut Grep null Aufrufer im gesamten Repository — die Passwort-Policy wird nirgends angewendet. `internal/authtoken/flow.go:31-36` und `internal/auth/changepassword.go:79` übernehmen ein neues Passwort ohne vorherigen Policy-Check. Zusätzlich wird nach einem Passwort-Reset `RevokeAllForUser` nicht aufgerufen — bestehende Sessions und Tokens bleiben nach einem Reset gültig (verschärft durch die 90-Tage-TTL aus SEC-01).\n\n**Technische Grundlage:** internal/pwpolicy/policy.go, internal/authtoken/flow.go, internal/auth/changepassword.go\n\n## Produkt-DNA für diesen Bereich\n**Übernehmen:**\n- Zitadel: jede Passwortänderung ist ein sicherheitskritisches Ereignis mit vollständigem Session-Invalidation-Trail.\n\n**Bewusst vermeiden:**\n- Policy-Code, der im Repository existiert, aber nie aufgerufen wird.\n\n\n## Ausgangslage\nSetzt auf folgenden bereits fertigen Kacheln auf: SEC-01.\n\n## Akzeptanzkriterien\nDie Kachel gilt erst als umgesetzt, wenn alle Punkte erfüllt sind:\n1. Jede Stelle, die ein neues Klartext-Passwort übernimmt (Setup, Change, Reset), ruft vorher `pwpolicy.Validate` auf; ein die Policy verletzendes Passwort wird mit klarer Fehlermeldung abgelehnt\n2. Nach erfolgreichem Passwort-Reset (`authtoken`-Flow) ruft der Handler `RevokeAllForUser` für den betroffenen Nutzer auf\n3. Nach erfolgreichem Passwort-Change (`changepassword.go`) gilt dieselbe Widerrufspflicht bereits (Bestandscode prüfen) oder wird ergänzt, falls dort ebenfalls eine Lücke besteht\n\n## Prüfungen vor der Abnahme\nFühre jede Prüfung tatsächlich durch und halte das Ergebnis schriftlich fest. Nicht durchgeführte Prüfungen zählen als offen:\n1. Passwort-Setup/-Change/-Reset mit einem Policy-verletzenden Passwort (z. B. zu kurz) — muss vor dem Fix durchgehen und nach dem Fix abgelehnt werden\n2. Vor Reset ein Token/Session ausstellen, Reset durchführen, danach das alte Token gegen eine geschützte Route prüfen — muss nach dem Fix 401 liefern\n3. Gültiges, policy-konformes Passwort wird nach dem Fix weiterhin erfolgreich übernommen\n\n## Bekannte Fehler vermeiden\n\nSecurity-Review (main-Branch, Fable 5.1, SEC): siehe Auftrag oben für Datei:Zeile-Belege. Ohne Fix bleibt der beschriebene Angriff möglich.\n\n## Nicht Bestandteil dieser Kachel\nDiese Punkte gehören ausdrücklich in andere Kacheln. Bereite höchstens die Schnittstelle dafür vor:\n- Inhaltliche Ausgestaltung der Passwort-Policy-Regeln selbst (besteht bereits in `pwpolicy.Validate`)\n\n## Arbeitsweise\n1. Lies zuerst den bestehenden Code der Vorbedingungen und halte dich an die dort etablierten Muster.\n2. Setze die kleinste Lösung um, die alle Akzeptanzkriterien erfüllt. Kein Umbau angrenzender Bereiche.\n3. Schreibe zu jedem Akzeptanzkriterium mindestens einen automatisierten Test.\n4. Führe alle Prüfungen aus und dokumentiere Zielwert und Ergebnis.\n5. Deutschsprachige Oberflächentexte, englischsprachige Bezeichner im Code.\n6. Keine Zugangsdaten, Schlüssel oder Verbindungszeichenfolgen im Code – ausschließlich über Umgebungsvariablen.\n\n## Abschluss\n- Commit-Nachricht: `SEC-04: passwort-policy-durchsetzung und session-widerruf nach reset`\n- Branch: `feature/sec-04-passwort-policy-durchsetzung-und-session-widerruf-nach-reset`\n- Schrittweise committen, Branch pushen, dann anhalten. Kein Merge, kein Deploy.\n- In der Beschreibung des Pull Request die durchgeführten Prüfungen mit Ergebnis auflisten.\n"
},
{
"code": "SEC-05",
"title": "Audit-Anbindung des kek-api-Einstiegspunkts aktivieren",
"epic": "SEC",
"featureIds": [],
"role": "Backend",
"complexity": "Einfach",
"estimateDays": 1,
"tech": "cmd/kek-api/main.go, internal/kek/store.go",
"description": "`cmd/kek-api/main.go:71` instanziiert `kek.NewStore(pool)` ohne `.WithAudit`. `internal/kek/store.go:231` schreibt den Eintrag `master_key_access` nur, wenn `s.audit != nil` ist — in `kek-api` ist das nie der Fall. Das verletzt die bereits in API-13 (Akzeptanzkriterium 2, Audit-Pflicht für Master-Key-Zugriffe) festgelegte Anforderung: Master-Key-Zugriffe über diesen Einstiegspunkt werden aktuell überhaupt nicht protokolliert.",
"dependsOn": [
"API-13"
],
"akzeptanz": [
"`cmd/kek-api/main.go` instanziiert `kek.NewStore` mit `.WithAudit`, unter Verwendung derselben Audit-Anbindung wie andere Core-Dienste",
"Jeder Master-Key-Zugriff über `kek-api` erzeugt nachweislich einen `master_key_access`-Audit-Eintrag mit Akteur, Zeitpunkt und betroffenem Tenant"
],
"pruefungen": [
"Master-Key-Zugriff über `kek-api` auslösen, danach Audit-Log abfragen — vor dem Fix: kein Eintrag, nach dem Fix: `master_key_access`-Eintrag mit korrektem Akteur/Tenant/Zeitpunkt vorhanden",
"Bestehende API-13-Prüfung (Audit-Event bei Tenant-KEK-Entschlüsselung) erneut gegen `kek-api` ausführen und grün bestätigen"
],
"status": "Backlog",
"epicName": "Sicherheit",
"epicEmoji": "🔒",
"wave": 12,
"unlocks": [],
"prompt": "# SEC-05 – Audit-Anbindung des kek-api-Einstiegspunkts aktivieren\n\n**Epic:** 🔒 Sicherheit | **Rolle:** Backend | **Komplexität:** Einfach | **Aufwand:** 1 Personentag(e) | **Welle:** 12\n\n---\n\n## Rolle\nDu bist erfahrene Entwicklerin im Bereich Backend und baust NEXARCH Core (Identity, Tenancy, Auth, Lizenzierung, Feature Flags, Audit, zentrale Konfiguration, API-Grundgerüst, Monitoring), das sich an Zitadel, Casbin, Unleash und Prometheus/Grafana orientiert und deren bekannte Schwächen bewusst vermeidet.\nServerseitige Logik, Datenzugriff und Schnittstelle. Keine Geschäftslogik in der Oberfläche.\n\n## Auftrag\n`cmd/kek-api/main.go:71` instanziiert `kek.NewStore(pool)` ohne `.WithAudit`. `internal/kek/store.go:231` schreibt den Eintrag `master_key_access` nur, wenn `s.audit != nil` ist — in `kek-api` ist das nie der Fall. Das verletzt die bereits in API-13 (Akzeptanzkriterium 2, Audit-Pflicht für Master-Key-Zugriffe) festgelegte Anforderung: Master-Key-Zugriffe über diesen Einstiegspunkt werden aktuell überhaupt nicht protokolliert.\n\n**Technische Grundlage:** cmd/kek-api/main.go, internal/kek/store.go\n\n## Produkt-DNA für diesen Bereich\n**Übernehmen:**\n- Zitadel: lückenloser Event-Trail zu jeder sicherheitskritischen Operation, auch über alle Einstiegspunkte hinweg.\n\n**Bewusst vermeiden:**\n- Audit-Pflicht, die in einem Modul existiert, aber an einem zweiten Einstiegspunkt desselben Dienstes vergessen wird.\n\n\n## Ausgangslage\nSetzt auf folgenden bereits fertigen Kacheln auf: API-13.\n\n## Akzeptanzkriterien\nDie Kachel gilt erst als umgesetzt, wenn alle Punkte erfüllt sind:\n1. `cmd/kek-api/main.go` instanziiert `kek.NewStore` mit `.WithAudit`, unter Verwendung derselben Audit-Anbindung wie andere Core-Dienste\n2. Jeder Master-Key-Zugriff über `kek-api` erzeugt nachweislich einen `master_key_access`-Audit-Eintrag mit Akteur, Zeitpunkt und betroffenem Tenant\n\n## Prüfungen vor der Abnahme\nFühre jede Prüfung tatsächlich durch und halte das Ergebnis schriftlich fest. Nicht durchgeführte Prüfungen zählen als offen:\n1. Master-Key-Zugriff über `kek-api` auslösen, danach Audit-Log abfragen — vor dem Fix: kein Eintrag, nach dem Fix: `master_key_access`-Eintrag mit korrektem Akteur/Tenant/Zeitpunkt vorhanden\n2. Bestehende API-13-Prüfung (Audit-Event bei Tenant-KEK-Entschlüsselung) erneut gegen `kek-api` ausführen und grün bestätigen\n\n## Bekannte Fehler vermeiden\n\nSecurity-Review (main-Branch, Fable 5.1, SEC): siehe Auftrag oben für Datei:Zeile-Belege. Ohne Fix bleibt der beschriebene Angriff möglich.\n\n## Nicht Bestandteil dieser Kachel\nDiese Punkte gehören ausdrücklich in andere Kacheln. Bereite höchstens die Schnittstelle dafür vor:\n- Änderungen am Audit-Datenmodell selbst (besteht bereits über AUD-01/API-13)\n\n## Arbeitsweise\n1. Lies zuerst den bestehenden Code der Vorbedingungen und halte dich an die dort etablierten Muster.\n2. Setze die kleinste Lösung um, die alle Akzeptanzkriterien erfüllt. Kein Umbau angrenzender Bereiche.\n3. Schreibe zu jedem Akzeptanzkriterium mindestens einen automatisierten Test.\n4. Führe alle Prüfungen aus und dokumentiere Zielwert und Ergebnis.\n5. Deutschsprachige Oberflächentexte, englischsprachige Bezeichner im Code.\n6. Keine Zugangsdaten, Schlüssel oder Verbindungszeichenfolgen im Code – ausschließlich über Umgebungsvariablen.\n\n## Abschluss\n- Commit-Nachricht: `SEC-05: audit-anbindung des kek-api-einstiegspunkts aktivieren`\n- Branch: `feature/sec-05-audit-anbindung-des-kek-api-einstiegspunkts-aktivieren`\n- Schrittweise committen, Branch pushen, dann anhalten. Kein Merge, kein Deploy.\n- In der Beschreibung des Pull Request die durchgeführten Prüfungen mit Ergebnis auflisten.\n"
},
{
"code": "SEC-06",
"title": "Tenant-Bindung in RequireAuth bei geteiltem HS256-Secret",
"epic": "SEC",
"featureIds": [],
"role": "Backend",
"complexity": "Mittel",
"estimateDays": 2,
"tech": "internal/auth/middleware.go, internal/moduletoken/token.go (Referenzmuster), cmd/rbac-admin-api/main.go",
"description": "`internal/auth/middleware.go:26-56` prüft im HS256-Pfad keine Tenant-Bindung des Tokens — anders als `internal/moduletoken/token.go:316`, das dies korrekt tut. Relevant wird das konkret bei `cmd/rbac-admin-api/main.go:43` (`NEXARCH_RBACADMIN_JWT_SECRET`): falls dieses Secret zwischen mehreren Mandanten oder Instanzen geteilt wird, könnte ein für Mandant A ausgestelltes Token auch für Mandant B akzeptiert werden, da `RequireAuth` die Tenant-Zugehörigkeit nicht gegen die aufgerufene Instanz verifiziert.",
"dependsOn": [],
"akzeptanz": [
"`RequireAuth` prüft im HS256-Pfad die Tenant-Bindung des Tokens gegen die erwartete Tenant-Instanz, nach demselben Muster wie `moduletoken.Verify` (token.go:316)",
"Ein für Mandant A ausgestelltes Token wird von einer für Mandant B konfigurierten Instanz abgelehnt, auch wenn dasselbe HS256-Secret verwendet wird",
"Dokumentiert (Code-Kommentar oder Config-Validierung beim Start), dass das JWT-Secret strikt pro Mandant/Instanz unterschiedlich sein muss, falls die Tenant-Bindungsprüfung aus technischen Gründen nicht vollständig greift"
],
"pruefungen": [
"Token für Mandant A mit geteiltem Secret ausstellen, gegen eine für Mandant B konfigurierte `RequireAuth`-Instanz verwenden — vor dem Fix: Zugriff gewährt, nach dem Fix: 401",
"Reguläres Token für die eigene Tenant-Instanz bleibt nach dem Fix gültig"
],
"status": "Backlog",
"epicName": "Sicherheit",
"epicEmoji": "🔒",
"wave": 12,
"unlocks": [],
"prompt": "# SEC-06 – Tenant-Bindung in RequireAuth bei geteiltem HS256-Secret\n\n**Epic:** 🔒 Sicherheit | **Rolle:** Backend | **Komplexität:** Mittel | **Aufwand:** 2 Personentag(e) | **Welle:** 12\n\n---\n\n## Rolle\nDu bist erfahrene Entwicklerin im Bereich Backend und baust NEXARCH Core (Identity, Tenancy, Auth, Lizenzierung, Feature Flags, Audit, zentrale Konfiguration, API-Grundgerüst, Monitoring), das sich an Zitadel, Casbin, Unleash und Prometheus/Grafana orientiert und deren bekannte Schwächen bewusst vermeidet.\nServerseitige Logik, Datenzugriff und Schnittstelle. Keine Geschäftslogik in der Oberfläche.\n\n## Auftrag\n`internal/auth/middleware.go:26-56` prüft im HS256-Pfad keine Tenant-Bindung des Tokens — anders als `internal/moduletoken/token.go:316`, das dies korrekt tut. Relevant wird das konkret bei `cmd/rbac-admin-api/main.go:43` (`NEXARCH_RBACADMIN_JWT_SECRET`): falls dieses Secret zwischen mehreren Mandanten oder Instanzen geteilt wird, könnte ein für Mandant A ausgestelltes Token auch für Mandant B akzeptiert werden, da `RequireAuth` die Tenant-Zugehörigkeit nicht gegen die aufgerufene Instanz verifiziert.\n\n**Technische Grundlage:** internal/auth/middleware.go, internal/moduletoken/token.go (Referenzmuster), cmd/rbac-admin-api/main.go\n\n## Produkt-DNA für diesen Bereich\n**Übernehmen:**\n- moduletoken-Muster: Tenant-Bindung ist fester Bestandteil jeder Token-Prüfung.\n\n**Bewusst vermeiden:**\n- Zwei unterschiedliche Verifikationspfade (RequireAuth vs. moduletoken) mit unterschiedlichem Sicherheitsniveau im selben System.\n\n\n## Ausgangslage\n\n## Akzeptanzkriterien\nDie Kachel gilt erst als umgesetzt, wenn alle Punkte erfüllt sind:\n1. `RequireAuth` prüft im HS256-Pfad die Tenant-Bindung des Tokens gegen die erwartete Tenant-Instanz, nach demselben Muster wie `moduletoken.Verify` (token.go:316)\n2. Ein für Mandant A ausgestelltes Token wird von einer für Mandant B konfigurierten Instanz abgelehnt, auch wenn dasselbe HS256-Secret verwendet wird\n3. Dokumentiert (Code-Kommentar oder Config-Validierung beim Start), dass das JWT-Secret strikt pro Mandant/Instanz unterschiedlich sein muss, falls die Tenant-Bindungsprüfung aus technischen Gründen nicht vollständig greift\n\n## Prüfungen vor der Abnahme\nFühre jede Prüfung tatsächlich durch und halte das Ergebnis schriftlich fest. Nicht durchgeführte Prüfungen zählen als offen:\n1. Token für Mandant A mit geteiltem Secret ausstellen, gegen eine für Mandant B konfigurierte `RequireAuth`-Instanz verwenden — vor dem Fix: Zugriff gewährt, nach dem Fix: 401\n2. Reguläres Token für die eigene Tenant-Instanz bleibt nach dem Fix gültig\n\n## Bekannte Fehler vermeiden\n\nSecurity-Review (main-Branch, Fable 5.1, SEC): siehe Auftrag oben für Datei:Zeile-Belege. Ohne Fix bleibt der beschriebene Angriff möglich.\n\n## Nicht Bestandteil dieser Kachel\nDiese Punkte gehören ausdrücklich in andere Kacheln. Bereite höchstens die Schnittstelle dafür vor:\n- Umstellung von HS256 auf asymmetrische Signaturverfahren (separates, größeres Thema)\n\n## Arbeitsweise\n1. Lies zuerst den bestehenden Code der Vorbedingungen und halte dich an die dort etablierten Muster.\n2. Setze die kleinste Lösung um, die alle Akzeptanzkriterien erfüllt. Kein Umbau angrenzender Bereiche.\n3. Schreibe zu jedem Akzeptanzkriterium mindestens einen automatisierten Test.\n4. Führe alle Prüfungen aus und dokumentiere Zielwert und Ergebnis.\n5. Deutschsprachige Oberflächentexte, englischsprachige Bezeichner im Code.\n6. Keine Zugangsdaten, Schlüssel oder Verbindungszeichenfolgen im Code – ausschließlich über Umgebungsvariablen.\n\n## Abschluss\n- Commit-Nachricht: `SEC-06: tenant-bindung in requireauth bei geteiltem hs256-secret`\n- Branch: `feature/sec-06-tenant-bindung-in-requireauth-bei-geteiltem-hs256-secret`\n- Schrittweise committen, Branch pushen, dann anhalten. Kein Merge, kein Deploy.\n- In der Beschreibung des Pull Request die durchgeführten Prüfungen mit Ergebnis auflisten.\n"
},
{
"code": "SEC-07",
"title": "Superadmin-Identität aus verifiziertem Claim statt Query-Parameter",
"epic": "SEC",
"featureIds": [],
"role": "Backend",
"complexity": "Mittel",
"estimateDays": 2,
"tech": "internal/tenantadmin/tenantadmin.go, internal/tenantadmin/tenantimportdirapi.go",
"description": "`internal/tenantadmin/tenantadmin.go:225,231,251,273` liest `q.Get(\"superadmin\")` direkt aus dem Query-String; `tenantimportdirapi.go:64,76` macht dasselbe. Jeder Inhaber eines gültigen Service-Tokens kann damit den im Audit-Log protokollierten Akteurs-Namen frei als beliebigen \"Superadmin\" fälschen — der Audit-Trail für hochprivilegierte Tenant-Admin-Operationen ist dadurch nicht vertrauenswürdig.",
"dependsOn": [],
"akzeptanz": [
"Alle vier Stellen in `tenantadmin.go` (Zeilen 225, 231, 251, 273) und beide Stellen in `tenantimportdirapi.go` (Zeilen 64, 76) beziehen die Superadmin-Identität aus einem verifizierten Claim des Service-Tokens, nicht aus `q.Get(\"superadmin\")`",
"Ein Request mit abweichendem `superadmin`-Query-Parameter wird entweder ignoriert (verifizierter Claim gewinnt) oder mit 4xx abgelehnt, nie stillschweigend als Akteursname übernommen",
"Audit-Einträge für diese Operationen enthalten nachweislich den aus dem Token verifizierten Namen, nicht den Query-Parameter"
],
"pruefungen": [
"Service-Token für Superadmin X verwenden, aber `?superadmin=Y` im Query-String setzen — vor dem Fix: Audit-Eintrag nennt Y, nach dem Fix: Audit-Eintrag nennt X (oder Request wird abgelehnt)",
"Request ohne gültiges Service-Token mit beliebigem `superadmin`-Parameter — muss abgelehnt werden",
"Regulärer Superadmin-Workflow mit korrektem Token bleibt funktionsfähig, Audit-Eintrag stimmt mit tatsächlichem Akteur überein"
],
"status": "Backlog",
"epicName": "Sicherheit",
"epicEmoji": "🔒",
"wave": 12,
"unlocks": [],
"prompt": "# SEC-07 – Superadmin-Identität aus verifiziertem Claim statt Query-Parameter\n\n**Epic:** 🔒 Sicherheit | **Rolle:** Backend | **Komplexität:** Mittel | **Aufwand:** 2 Personentag(e) | **Welle:** 12\n\n---\n\n## Rolle\nDu bist erfahrene Entwicklerin im Bereich Backend und baust NEXARCH Core (Identity, Tenancy, Auth, Lizenzierung, Feature Flags, Audit, zentrale Konfiguration, API-Grundgerüst, Monitoring), das sich an Zitadel, Casbin, Unleash und Prometheus/Grafana orientiert und deren bekannte Schwächen bewusst vermeidet.\nServerseitige Logik, Datenzugriff und Schnittstelle. Keine Geschäftslogik in der Oberfläche.\n\n## Auftrag\n`internal/tenantadmin/tenantadmin.go:225,231,251,273` liest `q.Get(\"superadmin\")` direkt aus dem Query-String; `tenantimportdirapi.go:64,76` macht dasselbe. Jeder Inhaber eines gültigen Service-Tokens kann damit den im Audit-Log protokollierten Akteurs-Namen frei als beliebigen \"Superadmin\" fälschen — der Audit-Trail für hochprivilegierte Tenant-Admin-Operationen ist dadurch nicht vertrauenswürdig.\n\n**Technische Grundlage:** internal/tenantadmin/tenantadmin.go, internal/tenantadmin/tenantimportdirapi.go\n\n## Produkt-DNA für diesen Bereich\n**Übernehmen:**\n- Zitadel: Audit-Akteur stammt immer aus einer kryptografisch verifizierten Identität, nie aus Client-Eingabe.\n\n**Bewusst vermeiden:**\n- Audit-Felder, die aus unauthentifizierten Request-Parametern befüllt werden.\n\n\n## Ausgangslage\n\n## Akzeptanzkriterien\nDie Kachel gilt erst als umgesetzt, wenn alle Punkte erfüllt sind:\n1. Alle vier Stellen in `tenantadmin.go` (Zeilen 225, 231, 251, 273) und beide Stellen in `tenantimportdirapi.go` (Zeilen 64, 76) beziehen die Superadmin-Identität aus einem verifizierten Claim des Service-Tokens, nicht aus `q.Get(\"superadmin\")`\n2. Ein Request mit abweichendem `superadmin`-Query-Parameter wird entweder ignoriert (verifizierter Claim gewinnt) oder mit 4xx abgelehnt, nie stillschweigend als Akteursname übernommen\n3. Audit-Einträge für diese Operationen enthalten nachweislich den aus dem Token verifizierten Namen, nicht den Query-Parameter\n\n## Prüfungen vor der Abnahme\nFühre jede Prüfung tatsächlich durch und halte das Ergebnis schriftlich fest. Nicht durchgeführte Prüfungen zählen als offen:\n1. Service-Token für Superadmin X verwenden, aber `?superadmin=Y` im Query-String setzen — vor dem Fix: Audit-Eintrag nennt Y, nach dem Fix: Audit-Eintrag nennt X (oder Request wird abgelehnt)\n2. Request ohne gültiges Service-Token mit beliebigem `superadmin`-Parameter — muss abgelehnt werden\n3. Regulärer Superadmin-Workflow mit korrektem Token bleibt funktionsfähig, Audit-Eintrag stimmt mit tatsächlichem Akteur überein\n\n## Bekannte Fehler vermeiden\n\nSecurity-Review (main-Branch, Fable 5.1, SEC): siehe Auftrag oben für Datei:Zeile-Belege. Ohne Fix bleibt der beschriebene Angriff möglich.\n\n## Nicht Bestandteil dieser Kachel\nDiese Punkte gehören ausdrücklich in andere Kacheln. Bereite höchstens die Schnittstelle dafür vor:\n- Einführung eines neuen Service-Token-Formats (nur Nutzung des bereits vorhandenen verifizierten Claims)\n\n## Arbeitsweise\n1. Lies zuerst den bestehenden Code der Vorbedingungen und halte dich an die dort etablierten Muster.\n2. Setze die kleinste Lösung um, die alle Akzeptanzkriterien erfüllt. Kein Umbau angrenzender Bereiche.\n3. Schreibe zu jedem Akzeptanzkriterium mindestens einen automatisierten Test.\n4. Führe alle Prüfungen aus und dokumentiere Zielwert und Ergebnis.\n5. Deutschsprachige Oberflächentexte, englischsprachige Bezeichner im Code.\n6. Keine Zugangsdaten, Schlüssel oder Verbindungszeichenfolgen im Code – ausschließlich über Umgebungsvariablen.\n\n## Abschluss\n- Commit-Nachricht: `SEC-07: superadmin-identität aus verifiziertem claim statt query-parameter`\n- Branch: `feature/sec-07-superadmin-identität-aus-verifiziertem-claim-statt-query-par`\n- Schrittweise committen, Branch pushen, dann anhalten. Kein Merge, kein Deploy.\n- In der Beschreibung des Pull Request die durchgeführten Prüfungen mit Ergebnis auflisten.\n"
},
{
"code": "SEC-08",
"title": "statuspage-devserver: offenes CORS ohne Auth aus systemd entfernen oder absichern",
"epic": "SEC",
"featureIds": [],
"role": "Backend",
"complexity": "Einfach",
"estimateDays": 1,
"tech": "cmd/statuspage-devserver/main.go, systemd-Unit-Definition",
"description": "`cmd/statuspage-devserver/main.go:48-58` setzt `Access-Control-Allow-Origin: *` ohne jede Authentifizierung. Der Dienst hat eine eigene systemd-Unit und kann damit trotz seines Namens dauerhaft laufen, nicht nur im Entwicklungskontext — ein offen erreichbarer, unauthentifizierter Endpunkt mit unbeschränktem CORS ist von jeder Webseite aus per Cross-Origin-Request auslesbar.",
"dependsOn": [],
"akzeptanz": [
"Entweder: die systemd-Unit für `statuspage-devserver` wird aus produktiven Deployment-Artefakten entfernt bzw. so markiert, dass sie nur in Entwicklungsumgebungen installiert wird",
"Oder: der Dienst erhält eine Authentifizierung (z. B. Service-Token) und ein auf konkrete, vertrauenswürdige Origins eingeschränktes CORS statt `*`",
"Die gewählte Lösung ist im Code bzw. in der Deployment-Dokumentation nachvollziehbar begründet"
],
"pruefungen": [
"Unauthentifizierten Cross-Origin-Request von einer beliebigen Origin gegen den Dienst stellen — vor dem Fix: Daten lesbar, nach dem Fix: entweder Dienst nicht produktiv erreichbar oder Request ohne gültige Auth/Origin abgelehnt",
"Falls Auth ergänzt: Zugriff mit gültigem Service-Token funktioniert weiterhin wie vorgesehen"
],
"status": "Backlog",
"epicName": "Sicherheit",
"epicEmoji": "🔒",
"wave": 12,
"unlocks": [],
"prompt": "# SEC-08 – statuspage-devserver: offenes CORS ohne Auth aus systemd entfernen oder absichern\n\n**Epic:** 🔒 Sicherheit | **Rolle:** Backend | **Komplexität:** Einfach | **Aufwand:** 1 Personentag(e) | **Welle:** 12\n\n---\n\n## Rolle\nDu bist erfahrene Entwicklerin im Bereich Backend und baust NEXARCH Core (Identity, Tenancy, Auth, Lizenzierung, Feature Flags, Audit, zentrale Konfiguration, API-Grundgerüst, Monitoring), das sich an Zitadel, Casbin, Unleash und Prometheus/Grafana orientiert und deren bekannte Schwächen bewusst vermeidet.\nServerseitige Logik, Datenzugriff und Schnittstelle. Keine Geschäftslogik in der Oberfläche.\n\n## Auftrag\n`cmd/statuspage-devserver/main.go:48-58` setzt `Access-Control-Allow-Origin: *` ohne jede Authentifizierung. Der Dienst hat eine eigene systemd-Unit und kann damit trotz seines Namens dauerhaft laufen, nicht nur im Entwicklungskontext — ein offen erreichbarer, unauthentifizierter Endpunkt mit unbeschränktem CORS ist von jeder Webseite aus per Cross-Origin-Request auslesbar.\n\n**Technische Grundlage:** cmd/statuspage-devserver/main.go, systemd-Unit-Definition\n\n## Produkt-DNA für diesen Bereich\n**Übernehmen:**\n- Prometheus/Grafana-Muster: Monitoring-Endpunkte sind standardmäßig nicht öffentlich ohne Auth erreichbar.\n\n**Bewusst vermeiden:**\n- Dev-Werkzeuge, die über eine eigene systemd-Unit faktisch zu produktiven Diensten werden.\n\n\n## Ausgangslage\n\n## Akzeptanzkriterien\nDie Kachel gilt erst als umgesetzt, wenn alle Punkte erfüllt sind:\n1. Entweder: die systemd-Unit für `statuspage-devserver` wird aus produktiven Deployment-Artefakten entfernt bzw. so markiert, dass sie nur in Entwicklungsumgebungen installiert wird\n2. Oder: der Dienst erhält eine Authentifizierung (z. B. Service-Token) und ein auf konkrete, vertrauenswürdige Origins eingeschränktes CORS statt `*`\n3. Die gewählte Lösung ist im Code bzw. in der Deployment-Dokumentation nachvollziehbar begründet\n\n## Prüfungen vor der Abnahme\nFühre jede Prüfung tatsächlich durch und halte das Ergebnis schriftlich fest. Nicht durchgeführte Prüfungen zählen als offen:\n1. Unauthentifizierten Cross-Origin-Request von einer beliebigen Origin gegen den Dienst stellen — vor dem Fix: Daten lesbar, nach dem Fix: entweder Dienst nicht produktiv erreichbar oder Request ohne gültige Auth/Origin abgelehnt\n2. Falls Auth ergänzt: Zugriff mit gültigem Service-Token funktioniert weiterhin wie vorgesehen\n\n## Bekannte Fehler vermeiden\n\nSecurity-Review (main-Branch, Fable 5.1, SEC): siehe Auftrag oben für Datei:Zeile-Belege. Ohne Fix bleibt der beschriebene Angriff möglich.\n\n## Nicht Bestandteil dieser Kachel\nDiese Punkte gehören ausdrücklich in andere Kacheln. Bereite höchstens die Schnittstelle dafür vor:\n- Funktionale Weiterentwicklung der Statuspage selbst\n\n## Arbeitsweise\n1. Lies zuerst den bestehenden Code der Vorbedingungen und halte dich an die dort etablierten Muster.\n2. Setze die kleinste Lösung um, die alle Akzeptanzkriterien erfüllt. Kein Umbau angrenzender Bereiche.\n3. Schreibe zu jedem Akzeptanzkriterium mindestens einen automatisierten Test.\n4. Führe alle Prüfungen aus und dokumentiere Zielwert und Ergebnis.\n5. Deutschsprachige Oberflächentexte, englischsprachige Bezeichner im Code.\n6. Keine Zugangsdaten, Schlüssel oder Verbindungszeichenfolgen im Code – ausschließlich über Umgebungsvariablen.\n\n## Abschluss\n- Commit-Nachricht: `SEC-08: statuspage-devserver: offenes cors ohne auth aus systemd entfernen oder absichern`\n- Branch: `feature/sec-08-statuspage-devserver:-offenes-cors-ohne-auth-aus-systemd-ent`\n- Schrittweise committen, Branch pushen, dann anhalten. Kein Merge, kein Deploy.\n- In der Beschreibung des Pull Request die durchgeführten Prüfungen mit Ergebnis auflisten.\n"
},
{
"code": "SEC-09",
"title": "Vertrauenswürdige Client-IP-Ermittlung für Rate-Limiting statt erster X-Forwarded-For-Eintrag",
"epic": "SEC",
"featureIds": [],
"role": "Backend",
"complexity": "Mittel",
"estimateDays": 2,
"tech": "internal/lockout/iplimit.go",
"description": "`internal/lockout/iplimit.go:97-98` verwendet den ERSTEN Eintrag in `X-Forwarded-For` als Client-IP für das Rate-Limiting. Dieser Header ist vom Client frei setzbar — ein Angreifer kann pro Anfrage einen beliebigen neuen \"Absender\" behaupten und damit das IP-basierte Rate-Limit (z. B. gegen Brute-Force bei Login) beliebig oft umgehen.",
"dependsOn": [],
"akzeptanz": [
"Die Client-IP für das Rate-Limiting wird aus einer vertrauenswürdigen Quelle ermittelt: entweder dem vom eigenen Reverse-Proxy gesetzten `X-Real-IP`, oder der letzten vertrauenswürdigen Adresse in der `X-Forwarded-For`-Kette (nicht der ersten, client-kontrollierten)",
"Ein Client, der beliebige zusätzliche Einträge vorne in `X-Forwarded-For` einfügt, kann das Rate-Limit nicht mehr durch Variation dieses Headers umgehen",
"Die Liste vertrauenswürdiger Proxy-Hops ist konfigurierbar (z. B. über Umgebungsvariable), nicht hartkodiert"
],
"pruefungen": [
"Wiederholte Login-Fehlversuche von derselben realen Verbindung, jeweils mit unterschiedlichem, selbst gesetztem erstem `X-Forwarded-For`-Eintrag — vor dem Fix: Rate-Limit greift nicht, nach dem Fix: Rate-Limit greift nach der konfigurierten Anzahl Versuche",
"Normale Anfragen über den echten Reverse-Proxy mit korrekter Hop-Kette werden weiterhin korrekt nach tatsächlicher Client-IP limitiert"
],
"status": "Backlog",
"epicName": "Sicherheit",
"epicEmoji": "🔒",
"wave": 12,
"unlocks": [],
"prompt": "# SEC-09 – Vertrauenswürdige Client-IP-Ermittlung für Rate-Limiting statt erster X-Forwarded-For-Eintrag\n\n**Epic:** 🔒 Sicherheit | **Rolle:** Backend | **Komplexität:** Mittel | **Aufwand:** 2 Personentag(e) | **Welle:** 12\n\n---\n\n## Rolle\nDu bist erfahrene Entwicklerin im Bereich Backend und baust NEXARCH Core (Identity, Tenancy, Auth, Lizenzierung, Feature Flags, Audit, zentrale Konfiguration, API-Grundgerüst, Monitoring), das sich an Zitadel, Casbin, Unleash und Prometheus/Grafana orientiert und deren bekannte Schwächen bewusst vermeidet.\nServerseitige Logik, Datenzugriff und Schnittstelle. Keine Geschäftslogik in der Oberfläche.\n\n## Auftrag\n`internal/lockout/iplimit.go:97-98` verwendet den ERSTEN Eintrag in `X-Forwarded-For` als Client-IP für das Rate-Limiting. Dieser Header ist vom Client frei setzbar — ein Angreifer kann pro Anfrage einen beliebigen neuen \"Absender\" behaupten und damit das IP-basierte Rate-Limit (z. B. gegen Brute-Force bei Login) beliebig oft umgehen.\n\n**Technische Grundlage:** internal/lockout/iplimit.go\n\n## Produkt-DNA für diesen Bereich\n**Übernehmen:**\n- Standard-Reverse-Proxy-Sicherheitsmuster: nur vertrauenswürdige, proxy-gesetzte Header fließen in sicherheitsrelevante Entscheidungen ein.\n\n**Bewusst vermeiden:**\n- Client-kontrollierte Header als Grundlage für sicherheitskritische Entscheidungen.\n\n\n## Ausgangslage\n\n## Akzeptanzkriterien\nDie Kachel gilt erst als umgesetzt, wenn alle Punkte erfüllt sind:\n1. Die Client-IP für das Rate-Limiting wird aus einer vertrauenswürdigen Quelle ermittelt: entweder dem vom eigenen Reverse-Proxy gesetzten `X-Real-IP`, oder der letzten vertrauenswürdigen Adresse in der `X-Forwarded-For`-Kette (nicht der ersten, client-kontrollierten)\n2. Ein Client, der beliebige zusätzliche Einträge vorne in `X-Forwarded-For` einfügt, kann das Rate-Limit nicht mehr durch Variation dieses Headers umgehen\n3. Die Liste vertrauenswürdiger Proxy-Hops ist konfigurierbar (z. B. über Umgebungsvariable), nicht hartkodiert\n\n## Prüfungen vor der Abnahme\nFühre jede Prüfung tatsächlich durch und halte das Ergebnis schriftlich fest. Nicht durchgeführte Prüfungen zählen als offen:\n1. Wiederholte Login-Fehlversuche von derselben realen Verbindung, jeweils mit unterschiedlichem, selbst gesetztem erstem `X-Forwarded-For`-Eintrag — vor dem Fix: Rate-Limit greift nicht, nach dem Fix: Rate-Limit greift nach der konfigurierten Anzahl Versuche\n2. Normale Anfragen über den echten Reverse-Proxy mit korrekter Hop-Kette werden weiterhin korrekt nach tatsächlicher Client-IP limitiert\n\n## Bekannte Fehler vermeiden\n\nSecurity-Review (main-Branch, Fable 5.1, SEC): siehe Auftrag oben für Datei:Zeile-Belege. Ohne Fix bleibt der beschriebene Angriff möglich.\n\n## Nicht Bestandteil dieser Kachel\nDiese Punkte gehören ausdrücklich in andere Kacheln. Bereite höchstens die Schnittstelle dafür vor:\n- Generelle Überarbeitung der Rate-Limiting-Schwellenwerte selbst\n\n## Arbeitsweise\n1. Lies zuerst den bestehenden Code der Vorbedingungen und halte dich an die dort etablierten Muster.\n2. Setze die kleinste Lösung um, die alle Akzeptanzkriterien erfüllt. Kein Umbau angrenzender Bereiche.\n3. Schreibe zu jedem Akzeptanzkriterium mindestens einen automatisierten Test.\n4. Führe alle Prüfungen aus und dokumentiere Zielwert und Ergebnis.\n5. Deutschsprachige Oberflächentexte, englischsprachige Bezeichner im Code.\n6. Keine Zugangsdaten, Schlüssel oder Verbindungszeichenfolgen im Code – ausschließlich über Umgebungsvariablen.\n\n## Abschluss\n- Commit-Nachricht: `SEC-09: vertrauenswürdige client-ip-ermittlung für rate-limiting statt erster x-forwarded-for-eintrag`\n- Branch: `feature/sec-09-vertrauenswürdige-client-ip-ermittlung-für-rate-limiting-sta`\n- Schrittweise committen, Branch pushen, dann anhalten. Kein Merge, kein Deploy.\n- In der Beschreibung des Pull Request die durchgeführten Prüfungen mit Ergebnis auflisten.\n"
}
]
}
+60
View File
@@ -0,0 +1,60 @@
# SEC-01 – Verbindliche Session-Revocation-Pruefung auf allen RequireAuth-Routen
**Epic:** 🔒 Sicherheit | **Rolle:** Backend | **Komplexität:** Mittel | **Aufwand:** 2 Personentag(e) | **Welle:** 12
---
## Rolle
Du bist erfahrene Entwicklerin im Bereich Backend und baust NEXARCH Core (Identity, Tenancy, Auth, Lizenzierung, Feature Flags, Audit, zentrale Konfiguration, API-Grundgerüst, Monitoring), das sich an Zitadel, Casbin, Unleash und Prometheus/Grafana orientiert und deren bekannte Schwächen bewusst vermeidet.
Serverseitige Logik, Datenzugriff und Schnittstelle. Keine Geschäftslogik in der Oberfläche.
## Auftrag
In `internal/auth/middleware.go:26,40` wird der Revocation-Status eines Tokens nur geprüft, wenn der aufrufende Code explizit einen `revocationStore`/Checker übergibt — Standardverhalten ist "nicht prüfen". In `cmd/account-api/main.go:212-217` registrieren `/account/me`, `/account/change-password` und `/auth/totp/*` `RequireAuth` ohne diesen Checker (nur `moduletoken.RegisterToken` in Zeile 203 übergibt ihn). `cmd/rbac-admin-api/main.go:73-80` und `cmd/notifyprefs-api/main.go:71-73` haben dieselbe Lücke. Zusammen mit der 90-Tage-Token-Laufzeit (`internal/auth/token.go:221`, `MobileAccessTokenTTL`) bedeutet das: ein gestohlenes Token bleibt nach Logout UND nach einem Passwortwechsel (der `RevokeAllForUser` auslöst) bis zu 90 Tage lang gültig für Profilzugriff, TOTP-Setup und die komplette RBAC-Admin-API.
**Technische Grundlage:** internal/auth/middleware.go, internal/auth/token.go, cmd/account-api, cmd/rbac-admin-api, cmd/notifyprefs-api
## Produkt-DNA für diesen Bereich
**Übernehmen:**
- Zitadel: Session-Widerruf wirkt sofort und lückenlos auf allen Routen, nicht nur ausgewählten.
**Bewusst vermeiden:**
- Keycloak – historisch inkonsistente Durchsetzung von Token-Revocation über verschiedene Endpunkte hinweg.
## Ausgangslage
## Akzeptanzkriterien
Die Kachel gilt erst als umgesetzt, wenn alle Punkte erfüllt sind:
1. `RequireAuth` prüft den Revocation-Status immer, wenn ein Revocation-Store im Prozess konfiguriert ist — kein optionaler Parameter mit Default "nicht prüfen" mehr; fehlt ein Store, scheitert der Prozessstart statt den Check stillschweigend zu überspringen
2. `cmd/account-api/main.go` übergibt an allen `RequireAuth`-Aufrufen (`/account/me`, `/account/change-password`, `/auth/totp/*`) denselben Revocation-Store wie `moduletoken.RegisterToken`
3. `cmd/rbac-admin-api/main.go` und `cmd/notifyprefs-api/main.go` übergeben ebenfalls den Revocation-Store an jeden `RequireAuth`-Aufruf
4. Ein nach Logout oder Passwortwechsel widerrufenes Token wird auf jeder dieser Routen mit 401 abgelehnt, nicht erst nach Ablauf der 90-Tage-TTL
## Prüfungen vor der Abnahme
Führe jede Prüfung tatsächlich durch und halte das Ergebnis schriftlich fest. Nicht durchgeführte Prüfungen zählen als offen:
1. Token ausstellen, Logout auslösen, danach `/account/me` mit demselben Token aufrufen — vor dem Fix: 200, nach dem Fix: 401, Nachweis per Request/Response-Log
2. Token ausstellen, Passwortwechsel auslösen (`RevokeAllForUser`), danach `/auth/totp/setup/begin` und einen RBAC-Admin-Endpunkt mit dem alten Token aufrufen — beide müssen 401 liefern
3. Architekturtest (analog API-21/INT-15-Muster), der einen `RequireAuth`-Aufruf ohne Revocation-Store im Build scheitern lässt, mit absichtlich eingefügtem Verstoß verifiziert und danach entfernt
## Bekannte Fehler vermeiden
Security-Review (main-Branch, Fable 5.1, SEC): siehe Auftrag oben für Datei:Zeile-Belege. Ohne Fix bleibt der beschriebene Angriff möglich.
## Nicht Bestandteil dieser Kachel
Diese Punkte gehören ausdrücklich in andere Kacheln. Bereite höchstens die Schnittstelle dafür vor:
- Verkürzung der 90-Tage-Mobile-Token-TTL selbst (separates Abwägungsthema Usability vs. Risiko, eigene Kachel falls gewünscht)
- 2FA-Downgrade-Problem aus demselben Befund (siehe SEC-02)
## Arbeitsweise
1. Lies zuerst den bestehenden Code der Vorbedingungen und halte dich an die dort etablierten Muster.
2. Setze die kleinste Lösung um, die alle Akzeptanzkriterien erfüllt. Kein Umbau angrenzender Bereiche.
3. Schreibe zu jedem Akzeptanzkriterium mindestens einen automatisierten Test.
4. Führe alle Prüfungen aus und dokumentiere Zielwert und Ergebnis.
5. Deutschsprachige Oberflächentexte, englischsprachige Bezeichner im Code.
6. Keine Zugangsdaten, Schlüssel oder Verbindungszeichenfolgen im Code – ausschließlich über Umgebungsvariablen.
## Abschluss
- Commit-Nachricht: `SEC-01: verbindliche session-revocation-pruefung auf allen requireauth-routen`
- Branch: `feature/sec-01-verbindliche-session-revocation-pruefung-auf-allen-requireau`
- Schrittweise committen, Branch pushen, dann anhalten. Kein Merge, kein Deploy.
- In der Beschreibung des Pull Request die durchgeführten Prüfungen mit Ergebnis auflisten.
+58
View File
@@ -0,0 +1,58 @@
# SEC-02 – Re-Authentifizierung vor TOTP-Setup/-Reset erzwingen
**Epic:** 🔒 Sicherheit | **Rolle:** Backend | **Komplexität:** Mittel | **Aufwand:** 2 Personentag(e) | **Welle:** 12
---
## Rolle
Du bist erfahrene Entwicklerin im Bereich Backend und baust NEXARCH Core (Identity, Tenancy, Auth, Lizenzierung, Feature Flags, Audit, zentrale Konfiguration, API-Grundgerüst, Monitoring), das sich an Zitadel, Casbin, Unleash und Prometheus/Grafana orientiert und deren bekannte Schwächen bewusst vermeidet.
Serverseitige Logik, Datenzugriff und Schnittstelle. Keine Geschäftslogik in der Oberfläche.
## Auftrag
`internal/totp/store.go:99-102` setzt per `ON CONFLICT (user_id) DO UPDATE SET secret = NULL, secret_ciphertext=$2, confirmed=false` die 2FA eines Nutzers zurück. `SetupBegin` in `internal/totp/handler.go` (ca. Zeile 164-185) verlangt dafür weder das aktuelle Passwort noch einen gültigen TOTP-Code — die Route liegt nur hinter `RequireAuth`. Damit kann `POST /auth/totp/setup/begin` mit einer gestohlenen Session (z. B. XSS oder Session-Cookie-Diebstahl) sofort `IsEnabled=false` setzen und die 2FA des Opfers deaktivieren, ohne dass bestehende Recovery-Codes invalidiert werden.
**Technische Grundlage:** internal/totp/handler.go, internal/totp/store.go
## Produkt-DNA für diesen Bereich
**Übernehmen:**
- Zitadel: sicherheitskritische Selbstverwaltungsaktionen (2FA-Änderung) verlangen Step-up-Authentifizierung.
**Bewusst vermeiden:**
- Keine 2FA-Verwaltung allein auf Basis einer bestehenden Session ohne Re-Auth.
## Ausgangslage
## Akzeptanzkriterien
Die Kachel gilt erst als umgesetzt, wenn alle Punkte erfüllt sind:
1. `SetupBegin` (und jede andere Route, die `confirmed=false`/`secret=NULL` setzen kann) verlangt zusätzlich zur gültigen Session eine frische Re-Authentifizierung: aktuelles Passwort ODER einen gültigen bestehenden TOTP-Code
2. Fehlt die Re-Authentifizierung oder ist sie falsch, bleibt der bestehende TOTP-Zustand (inklusive Recovery-Codes) unverändert und die Anfrage wird mit 401/403 abgelehnt
3. Ein erfolgreicher TOTP-Reset invalidiert die bisherigen Recovery-Codes explizit, statt sie unangetastet weiterbestehen zu lassen
## Prüfungen vor der Abnahme
Führe jede Prüfung tatsächlich durch und halte das Ergebnis schriftlich fest. Nicht durchgeführte Prüfungen zählen als offen:
1. `POST /auth/totp/setup/begin` nur mit gültiger Session und ohne Passwort/TOTP-Code aufrufen — muss vor dem Fix durchgehen (2FA wird deaktiviert) und nach dem Fix mit 401/403 scheitern, 2FA bleibt aktiv
2. Gleicher Aufruf mit korrektem aktuellem Passwort — muss erfolgreich das Setup beginnen
3. Nach erfolgreichem Reset: alte Recovery-Codes gegen den Recovery-Login-Endpunkt testen — müssen abgelehnt werden
## Bekannte Fehler vermeiden
Security-Review (main-Branch, Fable 5.1, SEC): siehe Auftrag oben für Datei:Zeile-Belege. Ohne Fix bleibt der beschriebene Angriff möglich.
## Nicht Bestandteil dieser Kachel
Diese Punkte gehören ausdrücklich in andere Kacheln. Bereite höchstens die Schnittstelle dafür vor:
- Revocation-Check auf `RequireAuth` selbst (siehe SEC-01, ist Voraussetzung aber eigene Kachel)
## Arbeitsweise
1. Lies zuerst den bestehenden Code der Vorbedingungen und halte dich an die dort etablierten Muster.
2. Setze die kleinste Lösung um, die alle Akzeptanzkriterien erfüllt. Kein Umbau angrenzender Bereiche.
3. Schreibe zu jedem Akzeptanzkriterium mindestens einen automatisierten Test.
4. Führe alle Prüfungen aus und dokumentiere Zielwert und Ergebnis.
5. Deutschsprachige Oberflächentexte, englischsprachige Bezeichner im Code.
6. Keine Zugangsdaten, Schlüssel oder Verbindungszeichenfolgen im Code – ausschließlich über Umgebungsvariablen.
## Abschluss
- Commit-Nachricht: `SEC-02: re-authentifizierung vor totp-setup/-reset erzwingen`
- Branch: `feature/sec-02-re-authentifizierung-vor-totp-setup/-reset-erzwingen`
- Schrittweise committen, Branch pushen, dann anhalten. Kein Merge, kein Deploy.
- In der Beschreibung des Pull Request die durchgeführten Prüfungen mit Ergebnis auflisten.
+59
View File
@@ -0,0 +1,59 @@
# SEC-03 – Modul-Tenant-Bindung und Delta-Validierung in resync-api
**Epic:** 🔒 Sicherheit | **Rolle:** Backend | **Komplexität:** Mittel | **Aufwand:** 2 Personentag(e) | **Welle:** 12
---
## Rolle
Du bist erfahrene Entwicklerin im Bereich Backend und baust NEXARCH Core (Identity, Tenancy, Auth, Lizenzierung, Feature Flags, Audit, zentrale Konfiguration, API-Grundgerüst, Monitoring), das sich an Zitadel, Casbin, Unleash und Prometheus/Grafana orientiert und deren bekannte Schwächen bewusst vermeidet.
Serverseitige Logik, Datenzugriff und Schnittstelle. Keine Geschäftslogik in der Oberfläche.
## Auftrag
`internal/resync/handler.go:52-60` verwirft den von `Authenticate` zurückgegebenen Modulnamen, statt ihn weiterzuverwenden. Zeile 90 ruft `h.audit.Record(ctx, e.TenantSlug, e.Actor, e.Action, e.Target, e.Metadata, e.OccurredAt)` mit `TenantSlug` direkt aus dem Request-Body auf; Zeile 120 ruft `usage.Increment(ctx, d.TenantSlug, d.Metric, d.Delta)` mit einem beliebigen `int64 Delta` auf, das auch negativ sein kann. Ein `IsActive(tenant, module)`-Check wie in `internal/kek/handler.go:853-869` fehlt komplett. Damit kann ein kompromittiertes Modul-Credential rückdatierte, nicht löschbare Audit-Einträge für fremde Mandanten erzeugen oder `storage_bytes` per negativem Delta künstlich auf 0 setzen (Quota-Umgehung).
**Technische Grundlage:** internal/resync/handler.go, internal/kek/handler.go (Referenzmuster)
## Produkt-DNA für diesen Bereich
**Übernehmen:**
- Casbin/Zitadel-Muster: jede privilegierte Schreiboperation prüft die tatsächliche Berechtigung des Akteurs, nie nur den Request-Inhalt.
**Bewusst vermeiden:**
- Vertrauen auf Client-/Request-geliefertes `TenantSlug` ohne serverseitige Bindungsprüfung.
## Ausgangslage
Setzt auf folgenden bereits fertigen Kacheln auf: API-13.
## Akzeptanzkriterien
Die Kachel gilt erst als umgesetzt, wenn alle Punkte erfüllt sind:
1. Vor jedem `audit.Record`- und `usage.Increment`-Aufruf prüft der Handler per `IsActive(tenant, module)` (analog kek-Muster), dass das authentifizierte Modul-Credential für genau diesen `TenantSlug` aktiv zugelassen ist
2. `TenantSlug` aus dem Request-Body wird gegen die tatsächliche Modul-Tenant-Zuordnung des Credentials validiert, nicht blind übernommen — Abweichung führt zu Ablehnung (4xx), nicht zu stillschweigender Korrektur
3. `Delta` in Usage-Increment-Requests wird auf einen sinnvollen Wertebereich begrenzt (kein beliebig negativer Wert, der eine Quota auf 0 oder negativ setzen kann); unzulässige Werte werden abgelehnt
## Prüfungen vor der Abnahme
Führe jede Prüfung tatsächlich durch und halte das Ergebnis schriftlich fest. Nicht durchgeführte Prüfungen zählen als offen:
1. Request mit gültigem Modul-Credential für Mandant A, aber `TenantSlug` von Mandant B im Body — vor dem Fix: Audit-Eintrag für B wird geschrieben, nach dem Fix: Ablehnung, kein Eintrag für B
2. Usage-Increment mit stark negativem `Delta` gegen eine bestehende Quota von Mandant A — vor dem Fix: Quota wird auf 0 oder negativ gesetzt, nach dem Fix: Ablehnung oder Begrenzung auf plausiblen Bereich
3. Regulärer Resync-Vorgang eines zugelassenen Moduls für den eigenen Mandanten bleibt nach dem Fix unverändert funktionsfähig
## Bekannte Fehler vermeiden
Security-Review (main-Branch, Fable 5.1, SEC): siehe Auftrag oben für Datei:Zeile-Belege. Ohne Fix bleibt der beschriebene Angriff möglich.
## Nicht Bestandteil dieser Kachel
Diese Punkte gehören ausdrücklich in andere Kacheln. Bereite höchstens die Schnittstelle dafür vor:
- Generelle Überarbeitung des resync-Protokolls jenseits der Tenant-Bindung und Delta-Validierung
## Arbeitsweise
1. Lies zuerst den bestehenden Code der Vorbedingungen und halte dich an die dort etablierten Muster.
2. Setze die kleinste Lösung um, die alle Akzeptanzkriterien erfüllt. Kein Umbau angrenzender Bereiche.
3. Schreibe zu jedem Akzeptanzkriterium mindestens einen automatisierten Test.
4. Führe alle Prüfungen aus und dokumentiere Zielwert und Ergebnis.
5. Deutschsprachige Oberflächentexte, englischsprachige Bezeichner im Code.
6. Keine Zugangsdaten, Schlüssel oder Verbindungszeichenfolgen im Code – ausschließlich über Umgebungsvariablen.
## Abschluss
- Commit-Nachricht: `SEC-03: modul-tenant-bindung und delta-validierung in resync-api`
- Branch: `feature/sec-03-modul-tenant-bindung-und-delta-validierung-in-resync-api`
- Schrittweise committen, Branch pushen, dann anhalten. Kein Merge, kein Deploy.
- In der Beschreibung des Pull Request die durchgeführten Prüfungen mit Ergebnis auflisten.
+59
View File
@@ -0,0 +1,59 @@
# SEC-04 – Passwort-Policy-Durchsetzung und Session-Widerruf nach Reset
**Epic:** 🔒 Sicherheit | **Rolle:** Backend | **Komplexität:** Mittel | **Aufwand:** 2 Personentag(e) | **Welle:** 12
---
## Rolle
Du bist erfahrene Entwicklerin im Bereich Backend und baust NEXARCH Core (Identity, Tenancy, Auth, Lizenzierung, Feature Flags, Audit, zentrale Konfiguration, API-Grundgerüst, Monitoring), das sich an Zitadel, Casbin, Unleash und Prometheus/Grafana orientiert und deren bekannte Schwächen bewusst vermeidet.
Serverseitige Logik, Datenzugriff und Schnittstelle. Keine Geschäftslogik in der Oberfläche.
## Auftrag
`internal/pwpolicy/policy.go:36` definiert `Validate`, hat aber laut Grep null Aufrufer im gesamten Repository — die Passwort-Policy wird nirgends angewendet. `internal/authtoken/flow.go:31-36` und `internal/auth/changepassword.go:79` übernehmen ein neues Passwort ohne vorherigen Policy-Check. Zusätzlich wird nach einem Passwort-Reset `RevokeAllForUser` nicht aufgerufen — bestehende Sessions und Tokens bleiben nach einem Reset gültig (verschärft durch die 90-Tage-TTL aus SEC-01).
**Technische Grundlage:** internal/pwpolicy/policy.go, internal/authtoken/flow.go, internal/auth/changepassword.go
## Produkt-DNA für diesen Bereich
**Übernehmen:**
- Zitadel: jede Passwortänderung ist ein sicherheitskritisches Ereignis mit vollständigem Session-Invalidation-Trail.
**Bewusst vermeiden:**
- Policy-Code, der im Repository existiert, aber nie aufgerufen wird.
## Ausgangslage
Setzt auf folgenden bereits fertigen Kacheln auf: SEC-01.
## Akzeptanzkriterien
Die Kachel gilt erst als umgesetzt, wenn alle Punkte erfüllt sind:
1. Jede Stelle, die ein neues Klartext-Passwort übernimmt (Setup, Change, Reset), ruft vorher `pwpolicy.Validate` auf; ein die Policy verletzendes Passwort wird mit klarer Fehlermeldung abgelehnt
2. Nach erfolgreichem Passwort-Reset (`authtoken`-Flow) ruft der Handler `RevokeAllForUser` für den betroffenen Nutzer auf
3. Nach erfolgreichem Passwort-Change (`changepassword.go`) gilt dieselbe Widerrufspflicht bereits (Bestandscode prüfen) oder wird ergänzt, falls dort ebenfalls eine Lücke besteht
## Prüfungen vor der Abnahme
Führe jede Prüfung tatsächlich durch und halte das Ergebnis schriftlich fest. Nicht durchgeführte Prüfungen zählen als offen:
1. Passwort-Setup/-Change/-Reset mit einem Policy-verletzenden Passwort (z. B. zu kurz) — muss vor dem Fix durchgehen und nach dem Fix abgelehnt werden
2. Vor Reset ein Token/Session ausstellen, Reset durchführen, danach das alte Token gegen eine geschützte Route prüfen — muss nach dem Fix 401 liefern
3. Gültiges, policy-konformes Passwort wird nach dem Fix weiterhin erfolgreich übernommen
## Bekannte Fehler vermeiden
Security-Review (main-Branch, Fable 5.1, SEC): siehe Auftrag oben für Datei:Zeile-Belege. Ohne Fix bleibt der beschriebene Angriff möglich.
## Nicht Bestandteil dieser Kachel
Diese Punkte gehören ausdrücklich in andere Kacheln. Bereite höchstens die Schnittstelle dafür vor:
- Inhaltliche Ausgestaltung der Passwort-Policy-Regeln selbst (besteht bereits in `pwpolicy.Validate`)
## Arbeitsweise
1. Lies zuerst den bestehenden Code der Vorbedingungen und halte dich an die dort etablierten Muster.
2. Setze die kleinste Lösung um, die alle Akzeptanzkriterien erfüllt. Kein Umbau angrenzender Bereiche.
3. Schreibe zu jedem Akzeptanzkriterium mindestens einen automatisierten Test.
4. Führe alle Prüfungen aus und dokumentiere Zielwert und Ergebnis.
5. Deutschsprachige Oberflächentexte, englischsprachige Bezeichner im Code.
6. Keine Zugangsdaten, Schlüssel oder Verbindungszeichenfolgen im Code – ausschließlich über Umgebungsvariablen.
## Abschluss
- Commit-Nachricht: `SEC-04: passwort-policy-durchsetzung und session-widerruf nach reset`
- Branch: `feature/sec-04-passwort-policy-durchsetzung-und-session-widerruf-nach-reset`
- Schrittweise committen, Branch pushen, dann anhalten. Kein Merge, kein Deploy.
- In der Beschreibung des Pull Request die durchgeführten Prüfungen mit Ergebnis auflisten.
+57
View File
@@ -0,0 +1,57 @@
# SEC-05 – Audit-Anbindung des kek-api-Einstiegspunkts aktivieren
**Epic:** 🔒 Sicherheit | **Rolle:** Backend | **Komplexität:** Einfach | **Aufwand:** 1 Personentag(e) | **Welle:** 12
---
## Rolle
Du bist erfahrene Entwicklerin im Bereich Backend und baust NEXARCH Core (Identity, Tenancy, Auth, Lizenzierung, Feature Flags, Audit, zentrale Konfiguration, API-Grundgerüst, Monitoring), das sich an Zitadel, Casbin, Unleash und Prometheus/Grafana orientiert und deren bekannte Schwächen bewusst vermeidet.
Serverseitige Logik, Datenzugriff und Schnittstelle. Keine Geschäftslogik in der Oberfläche.
## Auftrag
`cmd/kek-api/main.go:71` instanziiert `kek.NewStore(pool)` ohne `.WithAudit`. `internal/kek/store.go:231` schreibt den Eintrag `master_key_access` nur, wenn `s.audit != nil` ist — in `kek-api` ist das nie der Fall. Das verletzt die bereits in API-13 (Akzeptanzkriterium 2, Audit-Pflicht für Master-Key-Zugriffe) festgelegte Anforderung: Master-Key-Zugriffe über diesen Einstiegspunkt werden aktuell überhaupt nicht protokolliert.
**Technische Grundlage:** cmd/kek-api/main.go, internal/kek/store.go
## Produkt-DNA für diesen Bereich
**Übernehmen:**
- Zitadel: lückenloser Event-Trail zu jeder sicherheitskritischen Operation, auch über alle Einstiegspunkte hinweg.
**Bewusst vermeiden:**
- Audit-Pflicht, die in einem Modul existiert, aber an einem zweiten Einstiegspunkt desselben Dienstes vergessen wird.
## Ausgangslage
Setzt auf folgenden bereits fertigen Kacheln auf: API-13.
## Akzeptanzkriterien
Die Kachel gilt erst als umgesetzt, wenn alle Punkte erfüllt sind:
1. `cmd/kek-api/main.go` instanziiert `kek.NewStore` mit `.WithAudit`, unter Verwendung derselben Audit-Anbindung wie andere Core-Dienste
2. Jeder Master-Key-Zugriff über `kek-api` erzeugt nachweislich einen `master_key_access`-Audit-Eintrag mit Akteur, Zeitpunkt und betroffenem Tenant
## Prüfungen vor der Abnahme
Führe jede Prüfung tatsächlich durch und halte das Ergebnis schriftlich fest. Nicht durchgeführte Prüfungen zählen als offen:
1. Master-Key-Zugriff über `kek-api` auslösen, danach Audit-Log abfragen — vor dem Fix: kein Eintrag, nach dem Fix: `master_key_access`-Eintrag mit korrektem Akteur/Tenant/Zeitpunkt vorhanden
2. Bestehende API-13-Prüfung (Audit-Event bei Tenant-KEK-Entschlüsselung) erneut gegen `kek-api` ausführen und grün bestätigen
## Bekannte Fehler vermeiden
Security-Review (main-Branch, Fable 5.1, SEC): siehe Auftrag oben für Datei:Zeile-Belege. Ohne Fix bleibt der beschriebene Angriff möglich.
## Nicht Bestandteil dieser Kachel
Diese Punkte gehören ausdrücklich in andere Kacheln. Bereite höchstens die Schnittstelle dafür vor:
- Änderungen am Audit-Datenmodell selbst (besteht bereits über AUD-01/API-13)
## Arbeitsweise
1. Lies zuerst den bestehenden Code der Vorbedingungen und halte dich an die dort etablierten Muster.
2. Setze die kleinste Lösung um, die alle Akzeptanzkriterien erfüllt. Kein Umbau angrenzender Bereiche.
3. Schreibe zu jedem Akzeptanzkriterium mindestens einen automatisierten Test.
4. Führe alle Prüfungen aus und dokumentiere Zielwert und Ergebnis.
5. Deutschsprachige Oberflächentexte, englischsprachige Bezeichner im Code.
6. Keine Zugangsdaten, Schlüssel oder Verbindungszeichenfolgen im Code – ausschließlich über Umgebungsvariablen.
## Abschluss
- Commit-Nachricht: `SEC-05: audit-anbindung des kek-api-einstiegspunkts aktivieren`
- Branch: `feature/sec-05-audit-anbindung-des-kek-api-einstiegspunkts-aktivieren`
- Schrittweise committen, Branch pushen, dann anhalten. Kein Merge, kein Deploy.
- In der Beschreibung des Pull Request die durchgeführten Prüfungen mit Ergebnis auflisten.
+57
View File
@@ -0,0 +1,57 @@
# SEC-06 – Tenant-Bindung in RequireAuth bei geteiltem HS256-Secret
**Epic:** 🔒 Sicherheit | **Rolle:** Backend | **Komplexität:** Mittel | **Aufwand:** 2 Personentag(e) | **Welle:** 12
---
## Rolle
Du bist erfahrene Entwicklerin im Bereich Backend und baust NEXARCH Core (Identity, Tenancy, Auth, Lizenzierung, Feature Flags, Audit, zentrale Konfiguration, API-Grundgerüst, Monitoring), das sich an Zitadel, Casbin, Unleash und Prometheus/Grafana orientiert und deren bekannte Schwächen bewusst vermeidet.
Serverseitige Logik, Datenzugriff und Schnittstelle. Keine Geschäftslogik in der Oberfläche.
## Auftrag
`internal/auth/middleware.go:26-56` prüft im HS256-Pfad keine Tenant-Bindung des Tokens — anders als `internal/moduletoken/token.go:316`, das dies korrekt tut. Relevant wird das konkret bei `cmd/rbac-admin-api/main.go:43` (`NEXARCH_RBACADMIN_JWT_SECRET`): falls dieses Secret zwischen mehreren Mandanten oder Instanzen geteilt wird, könnte ein für Mandant A ausgestelltes Token auch für Mandant B akzeptiert werden, da `RequireAuth` die Tenant-Zugehörigkeit nicht gegen die aufgerufene Instanz verifiziert.
**Technische Grundlage:** internal/auth/middleware.go, internal/moduletoken/token.go (Referenzmuster), cmd/rbac-admin-api/main.go
## Produkt-DNA für diesen Bereich
**Übernehmen:**
- moduletoken-Muster: Tenant-Bindung ist fester Bestandteil jeder Token-Prüfung.
**Bewusst vermeiden:**
- Zwei unterschiedliche Verifikationspfade (RequireAuth vs. moduletoken) mit unterschiedlichem Sicherheitsniveau im selben System.
## Ausgangslage
## Akzeptanzkriterien
Die Kachel gilt erst als umgesetzt, wenn alle Punkte erfüllt sind:
1. `RequireAuth` prüft im HS256-Pfad die Tenant-Bindung des Tokens gegen die erwartete Tenant-Instanz, nach demselben Muster wie `moduletoken.Verify` (token.go:316)
2. Ein für Mandant A ausgestelltes Token wird von einer für Mandant B konfigurierten Instanz abgelehnt, auch wenn dasselbe HS256-Secret verwendet wird
3. Dokumentiert (Code-Kommentar oder Config-Validierung beim Start), dass das JWT-Secret strikt pro Mandant/Instanz unterschiedlich sein muss, falls die Tenant-Bindungsprüfung aus technischen Gründen nicht vollständig greift
## Prüfungen vor der Abnahme
Führe jede Prüfung tatsächlich durch und halte das Ergebnis schriftlich fest. Nicht durchgeführte Prüfungen zählen als offen:
1. Token für Mandant A mit geteiltem Secret ausstellen, gegen eine für Mandant B konfigurierte `RequireAuth`-Instanz verwenden — vor dem Fix: Zugriff gewährt, nach dem Fix: 401
2. Reguläres Token für die eigene Tenant-Instanz bleibt nach dem Fix gültig
## Bekannte Fehler vermeiden
Security-Review (main-Branch, Fable 5.1, SEC): siehe Auftrag oben für Datei:Zeile-Belege. Ohne Fix bleibt der beschriebene Angriff möglich.
## Nicht Bestandteil dieser Kachel
Diese Punkte gehören ausdrücklich in andere Kacheln. Bereite höchstens die Schnittstelle dafür vor:
- Umstellung von HS256 auf asymmetrische Signaturverfahren (separates, größeres Thema)
## Arbeitsweise
1. Lies zuerst den bestehenden Code der Vorbedingungen und halte dich an die dort etablierten Muster.
2. Setze die kleinste Lösung um, die alle Akzeptanzkriterien erfüllt. Kein Umbau angrenzender Bereiche.
3. Schreibe zu jedem Akzeptanzkriterium mindestens einen automatisierten Test.
4. Führe alle Prüfungen aus und dokumentiere Zielwert und Ergebnis.
5. Deutschsprachige Oberflächentexte, englischsprachige Bezeichner im Code.
6. Keine Zugangsdaten, Schlüssel oder Verbindungszeichenfolgen im Code – ausschließlich über Umgebungsvariablen.
## Abschluss
- Commit-Nachricht: `SEC-06: tenant-bindung in requireauth bei geteiltem hs256-secret`
- Branch: `feature/sec-06-tenant-bindung-in-requireauth-bei-geteiltem-hs256-secret`
- Schrittweise committen, Branch pushen, dann anhalten. Kein Merge, kein Deploy.
- In der Beschreibung des Pull Request die durchgeführten Prüfungen mit Ergebnis auflisten.
+58
View File
@@ -0,0 +1,58 @@
# SEC-07 – Superadmin-Identität aus verifiziertem Claim statt Query-Parameter
**Epic:** 🔒 Sicherheit | **Rolle:** Backend | **Komplexität:** Mittel | **Aufwand:** 2 Personentag(e) | **Welle:** 12
---
## Rolle
Du bist erfahrene Entwicklerin im Bereich Backend und baust NEXARCH Core (Identity, Tenancy, Auth, Lizenzierung, Feature Flags, Audit, zentrale Konfiguration, API-Grundgerüst, Monitoring), das sich an Zitadel, Casbin, Unleash und Prometheus/Grafana orientiert und deren bekannte Schwächen bewusst vermeidet.
Serverseitige Logik, Datenzugriff und Schnittstelle. Keine Geschäftslogik in der Oberfläche.
## Auftrag
`internal/tenantadmin/tenantadmin.go:225,231,251,273` liest `q.Get("superadmin")` direkt aus dem Query-String; `tenantimportdirapi.go:64,76` macht dasselbe. Jeder Inhaber eines gültigen Service-Tokens kann damit den im Audit-Log protokollierten Akteurs-Namen frei als beliebigen "Superadmin" fälschen — der Audit-Trail für hochprivilegierte Tenant-Admin-Operationen ist dadurch nicht vertrauenswürdig.
**Technische Grundlage:** internal/tenantadmin/tenantadmin.go, internal/tenantadmin/tenantimportdirapi.go
## Produkt-DNA für diesen Bereich
**Übernehmen:**
- Zitadel: Audit-Akteur stammt immer aus einer kryptografisch verifizierten Identität, nie aus Client-Eingabe.
**Bewusst vermeiden:**
- Audit-Felder, die aus unauthentifizierten Request-Parametern befüllt werden.
## Ausgangslage
## Akzeptanzkriterien
Die Kachel gilt erst als umgesetzt, wenn alle Punkte erfüllt sind:
1. Alle vier Stellen in `tenantadmin.go` (Zeilen 225, 231, 251, 273) und beide Stellen in `tenantimportdirapi.go` (Zeilen 64, 76) beziehen die Superadmin-Identität aus einem verifizierten Claim des Service-Tokens, nicht aus `q.Get("superadmin")`
2. Ein Request mit abweichendem `superadmin`-Query-Parameter wird entweder ignoriert (verifizierter Claim gewinnt) oder mit 4xx abgelehnt, nie stillschweigend als Akteursname übernommen
3. Audit-Einträge für diese Operationen enthalten nachweislich den aus dem Token verifizierten Namen, nicht den Query-Parameter
## Prüfungen vor der Abnahme
Führe jede Prüfung tatsächlich durch und halte das Ergebnis schriftlich fest. Nicht durchgeführte Prüfungen zählen als offen:
1. Service-Token für Superadmin X verwenden, aber `?superadmin=Y` im Query-String setzen — vor dem Fix: Audit-Eintrag nennt Y, nach dem Fix: Audit-Eintrag nennt X (oder Request wird abgelehnt)
2. Request ohne gültiges Service-Token mit beliebigem `superadmin`-Parameter — muss abgelehnt werden
3. Regulärer Superadmin-Workflow mit korrektem Token bleibt funktionsfähig, Audit-Eintrag stimmt mit tatsächlichem Akteur überein
## Bekannte Fehler vermeiden
Security-Review (main-Branch, Fable 5.1, SEC): siehe Auftrag oben für Datei:Zeile-Belege. Ohne Fix bleibt der beschriebene Angriff möglich.
## Nicht Bestandteil dieser Kachel
Diese Punkte gehören ausdrücklich in andere Kacheln. Bereite höchstens die Schnittstelle dafür vor:
- Einführung eines neuen Service-Token-Formats (nur Nutzung des bereits vorhandenen verifizierten Claims)
## Arbeitsweise
1. Lies zuerst den bestehenden Code der Vorbedingungen und halte dich an die dort etablierten Muster.
2. Setze die kleinste Lösung um, die alle Akzeptanzkriterien erfüllt. Kein Umbau angrenzender Bereiche.
3. Schreibe zu jedem Akzeptanzkriterium mindestens einen automatisierten Test.
4. Führe alle Prüfungen aus und dokumentiere Zielwert und Ergebnis.
5. Deutschsprachige Oberflächentexte, englischsprachige Bezeichner im Code.
6. Keine Zugangsdaten, Schlüssel oder Verbindungszeichenfolgen im Code – ausschließlich über Umgebungsvariablen.
## Abschluss
- Commit-Nachricht: `SEC-07: superadmin-identität aus verifiziertem claim statt query-parameter`
- Branch: `feature/sec-07-superadmin-identität-aus-verifiziertem-claim-statt-query-par`
- Schrittweise committen, Branch pushen, dann anhalten. Kein Merge, kein Deploy.
- In der Beschreibung des Pull Request die durchgeführten Prüfungen mit Ergebnis auflisten.
+57
View File
@@ -0,0 +1,57 @@
# SEC-08 – statuspage-devserver: offenes CORS ohne Auth aus systemd entfernen oder absichern
**Epic:** 🔒 Sicherheit | **Rolle:** Backend | **Komplexität:** Einfach | **Aufwand:** 1 Personentag(e) | **Welle:** 12
---
## Rolle
Du bist erfahrene Entwicklerin im Bereich Backend und baust NEXARCH Core (Identity, Tenancy, Auth, Lizenzierung, Feature Flags, Audit, zentrale Konfiguration, API-Grundgerüst, Monitoring), das sich an Zitadel, Casbin, Unleash und Prometheus/Grafana orientiert und deren bekannte Schwächen bewusst vermeidet.
Serverseitige Logik, Datenzugriff und Schnittstelle. Keine Geschäftslogik in der Oberfläche.
## Auftrag
`cmd/statuspage-devserver/main.go:48-58` setzt `Access-Control-Allow-Origin: *` ohne jede Authentifizierung. Der Dienst hat eine eigene systemd-Unit und kann damit trotz seines Namens dauerhaft laufen, nicht nur im Entwicklungskontext — ein offen erreichbarer, unauthentifizierter Endpunkt mit unbeschränktem CORS ist von jeder Webseite aus per Cross-Origin-Request auslesbar.
**Technische Grundlage:** cmd/statuspage-devserver/main.go, systemd-Unit-Definition
## Produkt-DNA für diesen Bereich
**Übernehmen:**
- Prometheus/Grafana-Muster: Monitoring-Endpunkte sind standardmäßig nicht öffentlich ohne Auth erreichbar.
**Bewusst vermeiden:**
- Dev-Werkzeuge, die über eine eigene systemd-Unit faktisch zu produktiven Diensten werden.
## Ausgangslage
## Akzeptanzkriterien
Die Kachel gilt erst als umgesetzt, wenn alle Punkte erfüllt sind:
1. Entweder: die systemd-Unit für `statuspage-devserver` wird aus produktiven Deployment-Artefakten entfernt bzw. so markiert, dass sie nur in Entwicklungsumgebungen installiert wird
2. Oder: der Dienst erhält eine Authentifizierung (z. B. Service-Token) und ein auf konkrete, vertrauenswürdige Origins eingeschränktes CORS statt `*`
3. Die gewählte Lösung ist im Code bzw. in der Deployment-Dokumentation nachvollziehbar begründet
## Prüfungen vor der Abnahme
Führe jede Prüfung tatsächlich durch und halte das Ergebnis schriftlich fest. Nicht durchgeführte Prüfungen zählen als offen:
1. Unauthentifizierten Cross-Origin-Request von einer beliebigen Origin gegen den Dienst stellen — vor dem Fix: Daten lesbar, nach dem Fix: entweder Dienst nicht produktiv erreichbar oder Request ohne gültige Auth/Origin abgelehnt
2. Falls Auth ergänzt: Zugriff mit gültigem Service-Token funktioniert weiterhin wie vorgesehen
## Bekannte Fehler vermeiden
Security-Review (main-Branch, Fable 5.1, SEC): siehe Auftrag oben für Datei:Zeile-Belege. Ohne Fix bleibt der beschriebene Angriff möglich.
## Nicht Bestandteil dieser Kachel
Diese Punkte gehören ausdrücklich in andere Kacheln. Bereite höchstens die Schnittstelle dafür vor:
- Funktionale Weiterentwicklung der Statuspage selbst
## Arbeitsweise
1. Lies zuerst den bestehenden Code der Vorbedingungen und halte dich an die dort etablierten Muster.
2. Setze die kleinste Lösung um, die alle Akzeptanzkriterien erfüllt. Kein Umbau angrenzender Bereiche.
3. Schreibe zu jedem Akzeptanzkriterium mindestens einen automatisierten Test.
4. Führe alle Prüfungen aus und dokumentiere Zielwert und Ergebnis.
5. Deutschsprachige Oberflächentexte, englischsprachige Bezeichner im Code.
6. Keine Zugangsdaten, Schlüssel oder Verbindungszeichenfolgen im Code – ausschließlich über Umgebungsvariablen.
## Abschluss
- Commit-Nachricht: `SEC-08: statuspage-devserver: offenes cors ohne auth aus systemd entfernen oder absichern`
- Branch: `feature/sec-08-statuspage-devserver:-offenes-cors-ohne-auth-aus-systemd-ent`
- Schrittweise committen, Branch pushen, dann anhalten. Kein Merge, kein Deploy.
- In der Beschreibung des Pull Request die durchgeführten Prüfungen mit Ergebnis auflisten.
+57
View File
@@ -0,0 +1,57 @@
# SEC-09 – Vertrauenswürdige Client-IP-Ermittlung für Rate-Limiting statt erster X-Forwarded-For-Eintrag
**Epic:** 🔒 Sicherheit | **Rolle:** Backend | **Komplexität:** Mittel | **Aufwand:** 2 Personentag(e) | **Welle:** 12
---
## Rolle
Du bist erfahrene Entwicklerin im Bereich Backend und baust NEXARCH Core (Identity, Tenancy, Auth, Lizenzierung, Feature Flags, Audit, zentrale Konfiguration, API-Grundgerüst, Monitoring), das sich an Zitadel, Casbin, Unleash und Prometheus/Grafana orientiert und deren bekannte Schwächen bewusst vermeidet.
Serverseitige Logik, Datenzugriff und Schnittstelle. Keine Geschäftslogik in der Oberfläche.
## Auftrag
`internal/lockout/iplimit.go:97-98` verwendet den ERSTEN Eintrag in `X-Forwarded-For` als Client-IP für das Rate-Limiting. Dieser Header ist vom Client frei setzbar — ein Angreifer kann pro Anfrage einen beliebigen neuen "Absender" behaupten und damit das IP-basierte Rate-Limit (z. B. gegen Brute-Force bei Login) beliebig oft umgehen.
**Technische Grundlage:** internal/lockout/iplimit.go
## Produkt-DNA für diesen Bereich
**Übernehmen:**
- Standard-Reverse-Proxy-Sicherheitsmuster: nur vertrauenswürdige, proxy-gesetzte Header fließen in sicherheitsrelevante Entscheidungen ein.
**Bewusst vermeiden:**
- Client-kontrollierte Header als Grundlage für sicherheitskritische Entscheidungen.
## Ausgangslage
## Akzeptanzkriterien
Die Kachel gilt erst als umgesetzt, wenn alle Punkte erfüllt sind:
1. Die Client-IP für das Rate-Limiting wird aus einer vertrauenswürdigen Quelle ermittelt: entweder dem vom eigenen Reverse-Proxy gesetzten `X-Real-IP`, oder der letzten vertrauenswürdigen Adresse in der `X-Forwarded-For`-Kette (nicht der ersten, client-kontrollierten)
2. Ein Client, der beliebige zusätzliche Einträge vorne in `X-Forwarded-For` einfügt, kann das Rate-Limit nicht mehr durch Variation dieses Headers umgehen
3. Die Liste vertrauenswürdiger Proxy-Hops ist konfigurierbar (z. B. über Umgebungsvariable), nicht hartkodiert
## Prüfungen vor der Abnahme
Führe jede Prüfung tatsächlich durch und halte das Ergebnis schriftlich fest. Nicht durchgeführte Prüfungen zählen als offen:
1. Wiederholte Login-Fehlversuche von derselben realen Verbindung, jeweils mit unterschiedlichem, selbst gesetztem erstem `X-Forwarded-For`-Eintrag — vor dem Fix: Rate-Limit greift nicht, nach dem Fix: Rate-Limit greift nach der konfigurierten Anzahl Versuche
2. Normale Anfragen über den echten Reverse-Proxy mit korrekter Hop-Kette werden weiterhin korrekt nach tatsächlicher Client-IP limitiert
## Bekannte Fehler vermeiden
Security-Review (main-Branch, Fable 5.1, SEC): siehe Auftrag oben für Datei:Zeile-Belege. Ohne Fix bleibt der beschriebene Angriff möglich.
## Nicht Bestandteil dieser Kachel
Diese Punkte gehören ausdrücklich in andere Kacheln. Bereite höchstens die Schnittstelle dafür vor:
- Generelle Überarbeitung der Rate-Limiting-Schwellenwerte selbst
## Arbeitsweise
1. Lies zuerst den bestehenden Code der Vorbedingungen und halte dich an die dort etablierten Muster.
2. Setze die kleinste Lösung um, die alle Akzeptanzkriterien erfüllt. Kein Umbau angrenzender Bereiche.
3. Schreibe zu jedem Akzeptanzkriterium mindestens einen automatisierten Test.
4. Führe alle Prüfungen aus und dokumentiere Zielwert und Ergebnis.
5. Deutschsprachige Oberflächentexte, englischsprachige Bezeichner im Code.
6. Keine Zugangsdaten, Schlüssel oder Verbindungszeichenfolgen im Code – ausschließlich über Umgebungsvariablen.
## Abschluss
- Commit-Nachricht: `SEC-09: vertrauenswürdige client-ip-ermittlung für rate-limiting statt erster x-forwarded-for-eintrag`
- Branch: `feature/sec-09-vertrauenswürdige-client-ip-ermittlung-für-rate-limiting-sta`
- Schrittweise committen, Branch pushen, dann anhalten. Kein Merge, kein Deploy.
- In der Beschreibung des Pull Request die durchgeführten Prüfungen mit Ergebnis auflisten.