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:
1 parent
b5f364fbde
commit
6d024e1d46
11 files changed
+863
-25
No files matched your search
+54
-13
@@ -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"
|
||||
|
+287
-12
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
Reference in new issue
Block a user