cd8c78d165
CI - Build & Test / Backend (.NET) (push) Successful in 45s
CI - Build & Test / Backend integration (PostgreSQL/Toxiproxy) (push) Failing after 1m0s
CI - Build & Test / Frontend (Vue/TS) (push) Successful in 2m49s
CI - Build & Test / Security Check (push) Successful in 7s
CI - Build & Test / Deploy Nexus (push) Has been skipped
155 lines
6.5 KiB
Markdown
155 lines
6.5 KiB
Markdown
# Nexus – Security Spot Check
|
||
|
||
> **Historische Evidenz:** Diese Datei hält den Stand vom 2026-07-26 fest. Der
|
||
> v0.2.60-Stabilitätsslice entfernte die ungenutzte CSRF-Tokenroute, ergänzte
|
||
> Browser-Origin-Prüfungen für Refresh/Logout und ersetzte den einfachen CI-Grep
|
||
> durch checksum-gepinntes Full-History-Gitleaks. Aktuelle Abnahmegrenzen stehen
|
||
> in `docs/audits/2026-07-31/stability-v0.2.60/IMPLEMENTATION_AND_ACCEPTANCE.md`.
|
||
|
||
**Datum:** 2026-07-26
|
||
**Commit:** `3bc7622977f4a6c2f2e98ab4aa856a2e45c3cf49`
|
||
**Vertraulichkeit:** Intern
|
||
**Scope:** Statische, risikoorientierte Prüfung von Authentifizierung,
|
||
Autorisierung, Bridge-Identität, Proxyweiterleitung und Credential-Lifecycle.
|
||
|
||
> Dies ist kein vollständiger Security Scan und kein Penetrationstest. Die
|
||
> Befunde sind codebasiert und müssen nach der Korrektur in einer realistischen
|
||
> Proxy-/Containerumgebung negativ getestet werden.
|
||
|
||
## Executive Summary
|
||
|
||
Zwei Zugriffskontrollbefunde blockieren eine verantwortbare
|
||
Produktionsfreigabe:
|
||
|
||
- Die API verlangt nicht global Authentifizierung. Mehrere Controller sind
|
||
dadurch standardmäßig öffentlich, darunter ein mutierender Agent-Endpunkt.
|
||
- Die Agent-Bridge akzeptiert eine bekannte, vom Client gesetzte
|
||
`X-Agent-Id` als ausreichenden Identitätsnachweis, während Nginx diesen Header
|
||
öffentlich weiterleitet.
|
||
|
||
Beide Probleme sind strukturell. Einzelne zusätzliche `[Authorize]`-Attribute
|
||
reichen nicht als dauerhafte Lösung; die sichere Voreinstellung muss
|
||
„authenticated by default“ lauten.
|
||
|
||
## SEC-001 – Fehlende globale Authorization Policy
|
||
|
||
**Schweregrad:** P0 / kritisch
|
||
**Konfidenz:** Hoch
|
||
**Betroffene Grenze:** Öffentliches `/api/v1` -> Controller
|
||
|
||
### Evidenz
|
||
|
||
- `backend/Extensions/ServiceCollectionExtensions.cs` registriert
|
||
`services.AddAuthorization()` ohne Fallback Policy.
|
||
- `backend/Program.cs` mappt sämtliche Controller.
|
||
- Mehrere Controller besitzen weder ein klassenweites `[Authorize]` noch
|
||
vollständige methodenspezifische Attribute.
|
||
- `backend/Controllers/AgentsController.cs` stellt
|
||
`POST /api/v1/agents/{id}/command` nur unter Rate Limiting bereit; die Methode
|
||
ruft den Agent Runtime Chat auf und schreibt einen Activity-Eintrag.
|
||
- `frontend/nginx.conf` leitet `/api/` an das Backend weiter.
|
||
- `README.md` behauptet gleichzeitig, alle `/api/v1`-Operationsrouten
|
||
benötigten ein Access Token. Implementierung und dokumentierter Vertrag
|
||
widersprechen sich.
|
||
|
||
### Auswirkung
|
||
|
||
Nicht authentifizierte Aufrufer können abhängig vom jeweiligen Controller
|
||
Betriebsdaten lesen. Im schwerwiegendsten verifizierten Fall kann ein Aufrufer
|
||
einem Agenten einen Command übermitteln und damit Laufzeit- und
|
||
Ressourcennutzung auslösen.
|
||
|
||
### Korrektur
|
||
|
||
1. Eine globale Fallback Policy einführen, die einen authentifizierten Principal
|
||
verlangt.
|
||
2. Ausschließlich wirklich öffentliche Endpunkte explizit mit
|
||
`[AllowAnonymous]` markieren, zum Beispiel Health und die notwendigen
|
||
Login-/Refresh-Routen.
|
||
3. Für mutierende oder sensitive Endpunkte zusätzlich Rollen-/Policy-Prüfungen
|
||
definieren.
|
||
4. Eine Controller-Inventur durchführen und jeden Endpunkt als public, owner,
|
||
admin, agent-service oder internal klassifizieren.
|
||
5. Negative Integrationstests hinzufügen: ohne Token muss jeder nicht
|
||
freigegebene Endpunkt `401` oder `403` liefern.
|
||
|
||
## SEC-002 – Caller-controlled `X-Agent-Id` gilt als Authentifizierung
|
||
|
||
**Schweregrad:** P0 / kritisch
|
||
**Konfidenz:** Hoch
|
||
**Betroffene Grenze:** Öffentliches `/api/bridge` -> Agent-Service-Identität
|
||
|
||
### Evidenz
|
||
|
||
- `GatewayBridgeController.TryResolveAgentAsync` akzeptiert eine
|
||
`X-Agent-Id`, sobald sie in der bekannten Actor-ID-Menge enthalten ist.
|
||
- Ein API-Key oder ein kryptografisch authentifizierter Service Principal ist
|
||
in diesem Pfad nicht erforderlich.
|
||
- `frontend/nginx.conf` übernimmt `X-Agent-Id` direkt aus dem eingehenden
|
||
Client-Header und leitet `/api/bridge/` weiter.
|
||
- Die Bridge bietet lesende und mutierende Task-/Activity-/Handoff-Commands.
|
||
- Bestehende Tests behandeln eine bekannte `X-Agent-Id` bewusst als
|
||
erfolgreichen Authentifizierungsfall.
|
||
|
||
### Auswirkung
|
||
|
||
Wer eine gültige Agent-ID kennt oder errät, kann sich gegenüber der Bridge als
|
||
dieser Agent ausgeben. Das gefährdet Task-Board-Integrität, Delegationen,
|
||
Activity-Audit-Trails und die Verlässlichkeit der Agentenidentität.
|
||
|
||
### Korrektur
|
||
|
||
1. Einen bekannten Identitätsheader niemals allein als Authentifizierung
|
||
akzeptieren.
|
||
2. Service-Traffic über mindestens einen starken Nachweis absichern:
|
||
rotierbarer Service-Key mit konstanter Zeitprüfung, mTLS, signiertes
|
||
Gateway-Token oder eine private Netzwerkgrenze mit authentifizierendem
|
||
Proxy.
|
||
3. Am äußeren Proxy eingehende `X-Agent-Id` entfernen. Der vertrauenswürdige
|
||
Proxy darf sie erst nach erfolgreicher Authentifizierung neu setzen.
|
||
4. `X-Agent-Id` nur als Actor-Attribut innerhalb eines bereits
|
||
authentifizierten Service-Contexts verwenden.
|
||
5. Replay-, falsche-ID-, fehlender-Key- und Proxy-Bypass-Tests ergänzen.
|
||
|
||
## SEC-003 – Temporäres Owner-Passwort im Prozess-Log
|
||
|
||
**Schweregrad:** P2 / mittel
|
||
**Konfidenz:** Hoch
|
||
**Betroffene Grenze:** Bootstrap -> Log-Aggregation / Betreiberzugriff
|
||
|
||
### Evidenz
|
||
|
||
Beim ersten Seed wird ein generiertes temporäres Owner-Passwort einmalig in
|
||
stderr ausgegeben. Obwohl das Passwort nicht in Git gespeichert wird, können
|
||
Container-, CI- oder zentrale Logs den Wert länger aufbewahren als vorgesehen.
|
||
|
||
### Korrektur
|
||
|
||
- Passwort über einen expliziten One-Time-Secret-Kanal bereitstellen oder einen
|
||
zeitlich eng begrenzten Reset-Link verwenden.
|
||
- Wenn Logging vorerst bleibt: Zugriff, Aufbewahrung und automatische
|
||
Redaction verbindlich konfigurieren.
|
||
- Erstanmeldung zum sofortigen Passwortwechsel zwingen und Seed-Nutzung
|
||
auditieren.
|
||
|
||
## Positive Kontrollen
|
||
|
||
- JWT-Schlüssel müssen mindestens 32 Byte lang sein.
|
||
- Issuer, Audience, Signatur und Laufzeit werden geprüft; Clock Skew ist klein.
|
||
- Refresh Tokens werden rotiert und gehasht persistiert.
|
||
- Sichere Cookie- und CSRF-Bausteine sind vorhanden.
|
||
- Auth- und Agent-Routen besitzen Rate-Limiting-Policies.
|
||
- Nginx setzt CSP, Frame-, MIME-, Referrer- und Permissions-Header.
|
||
- CI enthält eine einfache Suche nach versehentlich eingecheckten Secrets.
|
||
|
||
## Freigabekriterien
|
||
|
||
Eine Security-Freigabe ist erst möglich, wenn:
|
||
|
||
- beide P0-Pfade korrigiert sind,
|
||
- eine vollständige Endpoint-Policy-Matrix dokumentiert ist,
|
||
- negative Auth- und Bridge-Integrationstests in .NET 10 grün sind,
|
||
- der Test über dieselbe Nginx-/Traefik-Grenze wie Produktion erfolgt,
|
||
- unbekannte, bekannte-aber-nicht-authentifizierte und manipulierte
|
||
Agent-Identitäten zuverlässig abgewiesen werden.
|