Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Nächste Überarbeitung
Vorhergehende Überarbeitung
de:int:wvdsshell:notes:01-auth-architecture:auth-gateway [2026/03/05 14:22] – angelegt - Externe Bearbeitung 127.0.0.1de:int:wvdsshell:notes:01-auth-architecture:auth-gateway [Unbekanntes Datum] (aktuell) – gelöscht - Externe Bearbeitung (Unbekanntes Datum) 127.0.0.1
Zeile 1: Zeile 1:
-====== Gateway.Service — Auth-Erweiterungen für WvdS.Shell ====== 
- 
-//Stand: 2026-03-05// 
- 
-Übergeordnet: [[de:int:wvdsshell:notes:01-auth-architecture:start|Auth-Architektur — Gesamtübersicht]] 
-Verwandt: [[de:int:wvdsshell:notes:01-auth-architecture:auth-shell|2. WvdS.Shell]] | [[de:int:wvdsshell:notes:01-auth-architecture:auth-database|4. Datenschicht]] | [[de:int:wvdsshell:notes:01-auth-architecture:auth-migration|5. Migration]] 
- 
-===== Ausgangslage ===== 
- 
-Gateway.Service hat eine vollständige Middleware-Pipeline und ein Auth-Enum mit 
-allen benötigten Modi — jedoch ist nur ein Teil davon implementiert. 
- 
-==== Aktuell implementiert ==== 
- 
-^ Auth-Modus        ^ Status       ^ Anmerkung                                      ^ 
-| ''amNone''        | ✓ Aktiv      | Nur für DEV (''$IFDEF DEBUG''                | 
-| ''amBasic''       | ✓ Aktiv      | ConstantTimeEqual, CWE-208                     | 
-| ''amApiKey''      | ✓ Aktiv      | ConstantTimeEqual, Header konfigurierbar       | 
-| ''amBearer''      | — Reserviert | Interface vorhanden, nicht implementiert       | 
-| ''amCertificate'' | — Reserviert | Interface vorhanden, nicht implementiert       | 
-| ''amWindows''     | — Reserviert | Interface vorhanden, nicht implementiert       | 
- 
-==== Benötigt für WvdS.Shell ==== 
- 
-Für die Shell-Integration müssen folgende Komponenten neu implementiert werden: 
- 
-  - **PFX Install-ID Validierung** (''amCertificate'') 
-  - **Kerberos/SSPI Handler** (''amWindows'') 
-  - **JWT Ausstellung und Validierung** (''amBearer'') 
-  - **TOTP Service** (neu — für externen MFA-Flow) 
-  - **Refresh Token Management** (neu) 
-  - **Feature-Flags Endpoint** (neu — ENIVERSASYS-Anbindung) 
- 
-===== Neue Endpoints ===== 
- 
-Alle neuen Auth-Endpoints unter ''%%/api/v1/auth/%%''. 
-Ein neuer Controller ''WvdS.Data.Gateway.Controller.Auth'' wird benötigt. 
- 
-==== Übersicht ==== 
- 
-^ Verb   ^ Route                          ^ Auth erforderlich               ^ Beschreibung                           ^ 
-| POST   | ''/api/v1/auth/kerberos''      | Nein (ist der Auth-Schritt selbst) | Kerberos Negotiate-Handshake        | 
-| POST   | ''/api/v1/auth/external''      | Nein                            | Username/Passwort → TOTP-Challenge     | 
-| POST   | ''/api/v1/auth/totp/verify''   | Nein (Challenge-ID statt Token) | TOTP-Code einlösen → JWT              | 
-| POST   | ''/api/v1/auth/token/refresh'' | Refresh Token                   | Access Token erneuern                  | 
-| GET    | ''/api/v1/auth/totp/enroll''   | JWT Bearer                      | TOTP Enrollment-URI + QR generieren    | 
-| DELETE | ''/api/v1/auth/totp/enroll''   | JWT Bearer                      | TOTP-Enrollment zurücksetzen           | 
-| GET    | ''/api/v1/features''           | JWT Bearer                      | Feature-Flags für aktuelle Session     | 
- 
-==== POST /api/v1/auth/kerberos ==== 
- 
-Kerberos Negotiate-Handshake (mehrstufig, wie HTTP Negotiate definiert): 
- 
-<code> 
-Request: 
-  POST /api/v1/auth/kerberos 
-  Authorization: Negotiate <Base64-SPNEGO-Token> 
-  X-WvdS-Install-Id: ENIVERS-2026-001 
- 
-Response (ggf. mehrstufig): 
-  HTTP 401  Authorization: Negotiate <Base64-Challenge>   ← Server-Challenge 
-  HTTP 200  { "access_token": "...", "refresh_token": "...", "expires_in": 900 } 
-</code> 
- 
-==== POST /api/v1/auth/external ==== 
- 
-Erster Schritt des externen MFA-Flows. Gibt bei gültigem Username/Passwort 
-eine ''challenge_id'' zurück — noch kein Token. 
- 
-<code json> 
-Request: 
-  { "username": "max.muster", "password": "...", "install_id": "ENIVERS-2026-001" } 
- 
-Response 200: 
-  { "mfa_required": true, "challenge_id": "c7f3a..." } 
- 
-Response 401: 
-  { "error": "invalid_credentials" } 
-</code> 
- 
-Passwort wird nach Validierung sofort via ''FillChar'' zeroiert (CWE-316). 
- 
-==== POST /api/v1/auth/totp/verify ==== 
- 
-Zweiter Schritt des externen MFA-Flows. Löst die ''challenge_id'' gegen 
-den TOTP-Code ein und gibt bei Erfolg die Token-Pair aus. 
- 
-<code json> 
-Request: 
-  { "challenge_id": "c7f3a...", "code": "482910" } 
- 
-Response 200: 
-  { "access_token": "...", "refresh_token": "...", "expires_in": 900 } 
- 
-Response 401: 
-  { "error": "invalid_code", "attempts_remaining": 2 } 
-</code> 
- 
-==== POST /api/v1/auth/token/refresh ==== 
- 
-Silent Token Refresh. Refresh-Token wird im Request-Body oder als 
-''Authorization: Refresh <token>'' übergeben. 
- 
-<code json> 
-Request: 
-  { "refresh_token": "..." } 
- 
-Response 200: 
-  { "access_token": "...", "expires_in": 900 } 
- 
-Response 401: 
-  { "error": "refresh_token_expired" } 
-</code> 
- 
-==== GET /api/v1/features ==== 
- 
-Lädt die Feature-Flags für den authentifizierten User/die Installation. 
-Liest aktuell aus ENIVERSCAFM (''auth.*''-Namespace), zukünftig aus ENIVERSASYS. 
- 
-<code json> 
-Response 200: 
-  { "features": ["grids", "charts", "scheduling", "docking"] } 
-</code> 
- 
-===== JWT — Ausstellung und Validierung ===== 
- 
-==== Signaturalgorithmus ==== 
- 
-JWT wird mit **ML-DSA-65** signiert — die Bibliothek ist im Gateway bereits 
-vorhanden (''WvdS.Security.Cryptography''). Kein externer Abhängigkeitsbedarf. 
- 
-Standard-Header: ''{{"alg": "ML-DSA-65", "typ": "JWT"}}'' 
- 
-==== Payload-Struktur ==== 
- 
-<code json> 
-{ 
-  "sub":        "max.muster@corp.example.com", 
-  "install_id": "ENIVERS-2026-001", 
-  "auth_method": "windows", 
-  "mfa":        false, 
-  "iat":        1741132800, 
-  "exp":        1741133700, 
-  "jti":        "a3f9..." 
-} 
-</code> 
- 
-Feature-Flags werden **nicht** im JWT mitgeführt — sie werden bei Bedarf 
-frisch vom ''/api/v1/features''-Endpoint geladen. Grund: Flags können sich 
-ändern ohne dass ein neues Token ausgestellt werden muss. 
- 
-==== Token-Lebensdauer ==== 
- 
-^ Token          ^ Lebensdauer ^ Speicherort (Client)               ^ 
-| Access Token   | 15 Minuten  | RAM (Shell SessionManager)         | 
-| Refresh Token  | 30 Tage     | DPAPI SecretStorage                | 
-| TOTP Challenge | 5 Minuten   | Gateway-RAM (nach Ablauf gelöscht) | 
- 
-==== Validierung in Middleware ==== 
- 
-''TWvdSGatewayAuthHandler'' wird erweitert: Bei ''amBearer'' wird der JWT-Header 
-extrahiert, ML-DSA-Signatur geprüft, ''exp''-Claim validiert und ''install_id'' 
-gegen die PFX-Registrierung abgeglichen. 
- 
-===== TOTP Service ===== 
- 
-==== Zuständigkeit ==== 
- 
-Die gesamte TOTP-Logik liegt im Gateway — die Shell liefert nur den Code 
-(6 Ziffern). Details: [[de:int:wvdsshell:notes:01-auth-architecture:auth-shell#totp_aufgabenteilung|2. Shell-Doku: TOTP Aufgabenteilung]]. 
- 
-==== Implementierungsdetails ==== 
- 
-**Algorithmus:** HMAC-SHA1 (RFC 6238) über OpenSSL (''WvdS.Security.Cryptography'') — kein neuer Crypto-Code nötig. 
- 
-**Shared Secret Speicherung:** 
-  * Pro User ein Base32-kodiertes Secret 
-  * Gespeichert AES-256-GCM-verschlüsselt in der Gateway-Datenbank (CWE-256) 
-  * Kein Plaintext auf Disk 
- 
-**Validierungsfenster:** ±1 Zeitfenster (30 s × 3 = 90 s Toleranz für Uhrabweichung) 
- 
-**Lockout-Policy:** Bestehender ''FailedAuthPerMinute''-Alert-Counter wird genutzt. 
-Nach 5 Fehlversuchen: Challenge ungültig, neuer ''POST /api/v1/auth/external'' nötig. 
- 
-**Challenge-Lifetime:** 5 Minuten. Challenges in Gateway-RAM (''TDictionary<String, TChallengeEntry>''), kein DB-Roundtrip. 
- 
-==== Enrollment Flow ==== 
- 
-<code> 
-Admin-seitig: 
-  GET /api/v1/auth/totp/enroll   (JWT des Admin-Users) 
-  → { "otpauth_uri": "otpauth://totp/...", "qr_base64": "..." } 
-  → Admin zeigt QR dem User (oder schickt URI per E-Mail) 
- 
-Self-Service (Shell öffnet WebView): 
-  Shell navigiert zu: https://gateway/auth/enroll?token=<JWT> 
-  → Gateway zeigt HTML-Seite mit QR-Code 
-  → User scannt mit MS Authenticator 
-  → Enrollment-Bestätigung (User gibt ersten Code ein zum Verifizieren) 
-</code> 
- 
-===== PFX Install-ID Validierung ===== 
- 
-Das Gateway prüft bei jedem Auth-Request ob die ''install_id'' aus dem 
-PFX registriert und aktiv ist. 
- 
-==== Header-Konvention ==== 
- 
-<code> 
-X-WvdS-Install-Id: ENIVERS-2026-001 
-</code> 
- 
-Dieser Header wird von der Shell bei **jedem** Request mitgesendet — nicht nur beim Auth-Request. 
- 
-==== Validierungslogik ==== 
- 
-<code> 
-  1. X-WvdS-Install-Id Header vorhanden? 
-  2. install_id in Deployment-Tabelle (DB) vorhanden und aktiv? 
-  3. Zugehöriges Ablaufdatum noch nicht überschritten? 
-  4. → Validierung OK: weiter mit Auth-Flow 
-  5. → Validierung FAIL: HTTP 403 "unlicensed installation" 
-</code> 
- 
-Eine neue Tabelle (z. B. ''WVDS_DEPLOYMENTS'') in der Gateway-Datenbank speichert: 
- 
-^ Spalte           ^ Typ          ^ Beschreibung                         ^ 
-| ''INSTALL_ID''   | VARCHAR(64)  | PK — z. B. ''ENIVERS-2026-001''      | 
-| ''TENANT_NAME''  | VARCHAR(256) | Anzeigename                          | 
-| ''LICENSE_TIER'' | VARCHAR(32)  | ''professional'' / ''enterprise''    | 
-| ''VALID_UNTIL''  | DATE         | Ablaufdatum (aus PFX synchronisiert) | 
-| ''DOMAINS''      | VARCHAR(512) | Erlaubte AD-Domänen (Komma-getrennt) | 
-| ''ACTIVE''       | BIT          | Manuell deaktivierbar                | 
- 
-===== Kerberos/SSPI Handler ===== 
- 
-==== Standalone-Modus ==== 
- 
-Wenn Gateway als Windows Service läuft (nicht hinter IIS/NGINX): 
-  * ''AcceptSecurityContext'' aus ''Secur32.dll'' (Windows SSPI API) 
-  * HTTP ''Authorization: Negotiate'' Header — mehrstufiger Handshake 
-  * Nach Erfolg: Identität via ''QueryContextAttributes(SECPKG_ATTR_NAMES)'' lesen 
-  * → JWT für diese Identität ausstellen 
- 
-==== FastCGI-Modus (hinter IIS) ==== 
- 
-IIS übernimmt Windows Auth vollständig: 
-  * IIS sendet ''LOGON_USER'' als CGI-Umgebungsvariable 
-  * Gateway-FastCGI-Adapter (''WvdS.Data.Gateway.FastCGI.Adapter'') liest ''LOGON_USER'' 
-  * Kein ''AcceptSecurityContext'' im Gateway nötig — IIS hat bereits validiert 
-  * Direkt JWT ausstellen 
- 
-> **Empfehlung:** FastCGI hinter IIS für Corpnet-Deployments. 
-> Standalone SSPI für Self-hosted Szenarien (kleinere Installationen). 
- 
-===== Middleware-Änderungen ===== 
- 
-==== Bestehende Pipeline (9 Stufen) ==== 
- 
-<code> 
-1. TWvdSMetricsMiddleware 
-2. TWvdSLoggingMiddleware 
-3. TWvdSHttpsEnforcementMiddleware 
-4. TWvdSRequestSizeMiddleware 
-5. TWvdSRateLimitMiddleware 
-6. TWvdSGatewayAuthHandler          ← Erweiterung: amBearer + amWindows 
-7. TWvdSPayloadEncryptionMiddleware 
-8. TWvdSCompressionMiddleware 
-9. MapControllers 
-</code> 
- 
-==== Geplante Erweiterungen ==== 
- 
-^ Stufe ^ Komponente                        ^ Änderung                                               ^ 
-| 3.5   | ''TWvdSInstallIdMiddleware'' (neu) | ''X-WvdS-Install-Id'' prüfen, vor Auth                 | 
-| 6     | ''TWvdSGatewayAuthHandler''        | ''amBearer'': JWT ML-DSA validieren + Claims extrahieren | 
-| 6     | ''TWvdSGatewayAuthHandler''        | ''amWindows'': SSPI Negotiate oder LOGON_USER (FastCGI) | 
- 
-''TWvdSInstallIdMiddleware'' greift vor der eigentlichen Auth — ein Request ohne 
-gültige Install-ID wird mit HTTP 403 abgelehnt, bevor Credentials geprüft werden. 
- 
-===== Neue Units ===== 
- 
-<code> 
-src/ 
-  controllers/ 
-    WvdS.Data.Gateway.Controller.Auth.pas       ← /api/v1/auth/*, /api/v1/features 
-  core/ 
-    WvdS.Data.Gateway.Auth.JwtService.pas       ← JWT Ausstellung (ML-DSA-65), Validierung 
-    WvdS.Data.Gateway.Auth.TotpService.pas      ← Shared Secret, Challenge, HMAC-SHA1 (OpenSSL) 
-    WvdS.Data.Gateway.Auth.KerberosHandler.pas  ← SSPI AcceptSecurityContext (Windows-only) 
-    WvdS.Data.Gateway.Auth.CertValidator.pas    ← Install-ID gegen DB prüfen 
-    WvdS.Data.Gateway.Auth.RefreshStore.pas     ← Refresh Token (gehashed, DB-backed) 
-  middleware/ 
-    WvdS.Data.Gateway.Middleware.InstallId.pas  ← X-WvdS-Install-Id Middleware 
-</code> 
- 
-Alle neuen Units im ''src/core/'' und ''src/middleware/'' — passend zur 
-bestehenden Schichtenstruktur. 
- 
-===== Security — CWE-Ergänzungen ===== 
- 
-Zusätzlich zu den bestehenden 22 CWEs kommen durch die Auth-Erweiterung: 
- 
-^ CWE     ^ Beschreibung                    ^ Implementierung                                                          ^ 
-| CWE-287 | Improper Authentication         | Kerberos-Ticket muss vollständig validiert sein (nicht nur Presence-Check) | 
-| CWE-290 | Token Spoofing                  | JWT-Signatur (ML-DSA-65) wird bei jedem Request geprüft                  | 
-| CWE-613 | Insufficient Session Expiration | Refresh Token: 30 Tage, serverseitig revokierbar                         | 
-| CWE-620 | Unverified Password Change      | TOTP-Enrollment erfordert bestätigten ersten Code                        | 
-| CWE-384 | Session Fixation                | ''jti'' (JWT ID) einmalig, Replay via ''jti''-Blacklist                  | 
- 
-===== Design-Entscheidungen (protokolliert) ===== 
- 
-^ Thema                  ^ Entscheidung                                                       ^ 
-| JWT-Signatur           | ML-DSA-65 — Bibliothek bereits vorhanden, kein neuer Crypto-Code   | 
-| TOTP-Crypto            | HMAC-SHA1 via OpenSSL — kein neuer Crypto-Code                     | 
-| Kerberos Standalone    | ''Secur32.dll'' SSPI direkt; FastCGI-Modus bevorzugt hinter IIS    | 
-| Install-ID Transport   | HTTP-Header ''X-WvdS-Install-Id'' bei jedem Request                | 
-| Feature-Flags          | Nicht im JWT — separater Endpoint, frisch laden                    | 
-| Refresh Token Storage  | Gehashed (SHA-256) in DB — nie im Klartext gespeichert             | 
-| TOTP Enrollment        | Admin-seitig (primär) oder Self-Service WebView (BYOD)             | 
  
Zuletzt geändert: den 05.03.2026 um 14:22