From e34b321517bce8ac23b9fd9df21370d35b50552b Mon Sep 17 00:00:00 2001 From: Sebastian Frick Date: Thu, 19 Feb 2026 01:13:53 +0100 Subject: [PATCH] docs: add ticket for sec findings --- .../002-code-security-review-backend.md | 192 ++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 backend/docs/tickets/002-code-security-review-backend.md diff --git a/backend/docs/tickets/002-code-security-review-backend.md b/backend/docs/tickets/002-code-security-review-backend.md new file mode 100644 index 0000000..3cdc8e2 --- /dev/null +++ b/backend/docs/tickets/002-code-security-review-backend.md @@ -0,0 +1,192 @@ +# Ticket 002 - Komplettes Code- und Security-Review: Effigenix Backend + +**Datum:** 2026-02-19 +**Scope:** Gesamtes Backend (Security, Auth/AuthZ, API-Schutz, Secrets, Betriebssicherheit) +**Status:** Offen + +--- + +## Kritisch + +### K1 - Fehlende Autorisierung auf sensiblen User-Endpunkten (IDOR / Privilege Escalation) + +**Betroffene Dateien:** +- `src/main/java/de/effigenix/infrastructure/usermanagement/web/controller/UserController.java:200` +- `src/main/java/de/effigenix/infrastructure/usermanagement/web/controller/UserController.java:250` +- `src/main/java/de/effigenix/infrastructure/usermanagement/web/controller/UserController.java:313` +- `src/main/java/de/effigenix/infrastructure/usermanagement/web/controller/UserController.java:613` +- `src/main/java/de/effigenix/application/usermanagement/ListUsers.java:32` +- `src/main/java/de/effigenix/application/usermanagement/GetUser.java:25` +- `src/main/java/de/effigenix/application/usermanagement/UpdateUser.java:29` +- `src/main/java/de/effigenix/application/usermanagement/ChangePassword.java:36` + +**Problem:** +Mehrere User-Endpunkte haben kein `@PreAuthorize`, und die zugehörigen Use Cases prüfen weder Berechtigungen noch Ownership (`actorId` vs. Ziel-User) robust. Jeder authentifizierte Benutzer kann dadurch potenziell Benutzerdaten lesen und ändern. + +**Empfohlene Maßnahme:** +- Auf allen betroffenen Endpunkten explizite Policy erzwingen (`USER_READ`, `USER_WRITE` oder Self-Service-Policy). +- Zusätzlich serverseitige Ownership-/Scope-Prüfung im Application Layer einbauen (Defense in Depth). +- Negative Security-Tests ergänzen (fremden User lesen/ändern, Passwortänderung für fremden User). + +--- + +### K2 - Refresh-Token wird als Access-Token akzeptiert + +**Betroffene Dateien:** +- `src/main/java/de/effigenix/infrastructure/security/JwtTokenProvider.java:113` +- `src/main/java/de/effigenix/infrastructure/security/JwtTokenProvider.java:127` +- `src/main/java/de/effigenix/infrastructure/security/JwtSessionManager.java:89` +- `src/main/java/de/effigenix/infrastructure/security/JwtAuthenticationFilter.java:73` + +**Problem:** +Refresh-Tokens enthalten zwar `type=refresh`, bei Validierung und Request-Authentifizierung wird der Typ aber nicht geprüft. Dadurch können Refresh-Tokens als Bearer für API-Zugriffe missbraucht werden. + +**Empfohlene Maßnahme:** +- Token-Typ verpflichtend validieren (`access` nur im Request-Auth-Filter, `refresh` nur im Refresh-Flow). +- Separate Validierungsmethoden einführen (`validateAccessToken`, `validateRefreshToken`). +- Security-Tests ergänzen: Refresh-Token gegen geschützte Endpunkte muss 401 liefern. + +--- + +## Hoch + +### H1 - Unsichere Default-Credentials und Secrets im Standardprofil + +**Betroffene Dateien:** +- `src/main/resources/application.yml:6` +- `src/main/resources/application.yml:8` +- `src/main/resources/application.yml:26` +- `src/main/resources/application.yml:27` +- `src/main/resources/application.yml:31` +- `src/main/resources/db/changelog/changes/004-seed-admin-user.sql:3` +- `src/main/resources/db/changelog/changes/004-seed-admin-user.sql:12` +- `src/main/resources/db/changelog/changes/004-seed-admin-user.sql:26` + +**Problem:** +Statische Defaults für DB-Credentials, Security-User, JWT-Secret und Seed-Admin-Passwort sind im produktionsnahen Pfad hinterlegt. Bei fehlender Überschreibung entsteht ein direktes Übernahmerisiko. + +**Empfohlene Maßnahme:** +- Keine sicherheitsrelevanten Defaults im Standardprofil; App soll fail-fast starten, wenn Secrets fehlen. +- Seed-Admin ausschließlich über dediziertes Dev-/Bootstrap-Profil. +- Secret-Management über Vault/Kubernetes Secrets/CI Secret Store erzwingen. + +--- + +### H2 - Masterdata-Read-Endpunkte ohne Permission-Check + +**Betroffene Dateien:** +- `src/main/java/de/effigenix/infrastructure/masterdata/web/controller/ArticleController.java:95` +- `src/main/java/de/effigenix/infrastructure/masterdata/web/controller/ArticleController.java:121` +- `src/main/java/de/effigenix/infrastructure/masterdata/web/controller/CustomerController.java:99` +- `src/main/java/de/effigenix/infrastructure/masterdata/web/controller/CustomerController.java:125` +- `src/main/java/de/effigenix/infrastructure/masterdata/web/controller/SupplierController.java:90` +- `src/main/java/de/effigenix/infrastructure/masterdata/web/controller/SupplierController.java:113` +- `src/main/java/de/effigenix/infrastructure/masterdata/web/controller/ProductCategoryController.java:72` +- `src/main/resources/db/changelog/changes/008-add-masterdata-permissions.sql:1` + +**Problem:** +`MASTERDATA_READ` ist im Rollenmodell vorhanden, wird auf mehreren GET-Endpunkten aber nicht erzwungen. + +**Empfohlene Maßnahme:** +- Alle Read-Endpunkte mit `@PreAuthorize("hasAuthority('MASTERDATA_READ')")` absichern. +- Optional feiner aufteilen (`ARTICLE_READ`, `CUSTOMER_READ` etc.) für Least Privilege. +- Integrationstests für positive und negative Berechtigungsfälle ergänzen. + +--- + +## Mittel + +### M1 - Refresh-Flow funktional unvollständig (Session Lifecycle) + +**Betroffene Datei:** +- `src/main/java/de/effigenix/infrastructure/security/JwtSessionManager.java:141` + +**Problem:** +`refreshSession()` wirft `UnsupportedOperationException`. Der Endpoint ist öffentlich verfügbar, aber die Kernlogik fehlt. + +**Empfohlene Maßnahme:** +- Vollständigen Refresh-Flow implementieren: Refresh-Token validieren, User laden, Status prüfen, neues Token-Paar ausstellen. +- Rotation/Family-Tracking oder wenigstens Single-Use-Refresh-Tokens einführen. + +--- + +### M2 - Kein Brute-Force-Schutz auf Login + +**Betroffene Datei:** +- `src/main/java/de/effigenix/application/usermanagement/AuthenticateUser.java:42` + +**Problem:** +Keine Rate-Limits, kein Backoff, kein attempt-based Locking. Ermöglicht Credential-Stuffing und Passwort-Raten. + +**Empfohlene Maßnahme:** +- IP- und Username-basiertes Rate-Limiting (z. B. Bucket4j). +- Temporärer Lockout oder exponentielles Backoff nach Fehlversuchen. +- Security-Monitoring/Alerting auf auffällige Login-Muster. + +--- + +### M3 - Logout-Invalidierung nur in-memory, ohne Cleanup und ohne Cluster-Fähigkeit + +**Betroffene Dateien:** +- `src/main/java/de/effigenix/infrastructure/security/JwtSessionManager.java:35` +- `src/main/java/de/effigenix/infrastructure/security/JwtSessionManager.java:162` + +**Problem:** +Blacklist liegt nur im Prozessspeicher, ist nach Neustart weg und wächst ohne TTL/Cleanup. + +**Empfohlene Maßnahme:** +- Redis-basierte Blacklist mit TTL bis Token-Ablauf einsetzen. +- Alternativ zentraler Session Store + revocable token ids (`jti`). +- Cleanup-Strategie und Betriebsmetriken einführen. + +--- + +## Niedrig + +### N1 - Falsches Audit-Event bei fehlgeschlagenem Passwortwechsel + +**Betroffene Datei:** +- `src/main/java/de/effigenix/application/usermanagement/ChangePassword.java:53` + +**Problem:** +Bei falschem `currentPassword` wird `PASSWORD_CHANGED` statt eines Failure-Events geloggt. + +**Empfohlene Maßnahme:** +- Eigenes Event `PASSWORD_CHANGE_FAILED` verwenden. +- Audit-Event-Mapping für Erfolgs-/Fehlerpfade systematisch prüfen. + +--- + +### N2 - Verbose Fehler-/Debug-Ausgabe im Standardprofil + +**Betroffene Dateien:** +- `src/main/resources/application.yml:39` +- `src/main/resources/application.yml:46` +- `src/main/resources/application.yml:47` +- `src/main/resources/application.yml:48` + +**Problem:** +`include-message: always` plus DEBUG für Security und SQL im Standardprofil erhöht Risiko für Informationsabfluss. + +**Empfohlene Maßnahme:** +- Sichere Logging-Baseline für Produktivbetrieb (`INFO/WARN`, keine sensiblen Details). +- Fehlermeldungen für Clients generisch halten, Details nur serverseitig protokollieren. +- Profilabhängige Logging-Policies (`dev`, `test`, `prod`) trennen. + +--- + +## Test-/Betriebsnotiz + +### T1 - Security-Integrationstests in aktueller Umgebung nicht ausführbar + +**Nachweis:** +- `target/surefire-reports/de.effigenix.infrastructure.usermanagement.web.UserControllerIntegrationTest.txt` +- `target/surefire-reports/de.effigenix.infrastructure.usermanagement.web.SecurityIntegrationTest.txt` +- `target/surefire-reports/de.effigenix.infrastructure.usermanagement.web.AuthControllerIntegrationTest.txt` + +**Problem:** +Tests schlagen hier fehl, weil der Sandbox-Runner keinen lokalen Socket/Port für den Embedded Tomcat öffnen darf (`java.net.SocketException: Operation not permitted`). + +**Empfohlene Maßnahme:** +- Security-ITs in CI/Umgebung mit erlaubtem Port-Binding ausführen. +- Alternativ `@WebMvcTest`-basierte Tests ohne echten Server ergänzen, um AuthZ-Regeln isoliert zu prüfen.