# Sicherheitstest-Bericht: Jumphost Gateway **Datum:** 19.08.2026  |  **Version des Testobjekts:** Implementierung v0.2  |  **Tester:** Claude (im Auftrag von Midas) ## 1. Scope und Methodik ### 1.1 Was tatsächlich getestet wurde Diese Sitzung lief in einer isolierten Cloud-Sandbox ohne Zugriff auf ein reales Netzwerk, echte SSH-/RDP-Zielsysteme oder eine laufende `guacd`- Instanz. Der Test ist daher ein **Anwendungssicherheitstest (SAST + gezielter DAST) gegen den Quellcode und die lokal laufende Anwendung**, kein vollständiger Infrastruktur-/Netzwerk-Penetrationstest. Konkret durchgeführt: 1. **Statische Analyse (SAST):** `bandit` gegen den gesamten Python-Code (`app/`, `scripts/`), `pip-audit` gegen alle Produktiv- und Dev-Abhängigkeiten. 2. **Dynamischer Sicherheitstest (DAST, White-/Grey-Box):** 17 gezielte Angriffstests (`tests/test_pentest_security.py`) gegen die echte FastAPI-Anwendung inkl. SQLite-Datenbank, ausgeführt über den ASGI-Transport (kein echtes TCP/TLS, aber derselbe Anwendungscode wie in Produktion) sowie ergänzend manuelle Verifikation per `curl` gegen einen laufenden `uvicorn`-Prozess. 3. **Manuelle Code-Review-Punkte:** Session-/Cookie-Handling, RBAC- Durchsetzung, Key-Handling, Audit-Log-Integrität — im Rahmen der unter 2) genannten Testfälle verifiziert, nicht nur gelesen. ### 1.2 Was NICHT getestet wurde (out of scope in dieser Umgebung) | Bereich | Warum nicht möglich | |---|---| | Netzwerk-/Infrastruktur-Pentest (Portscans, TLS-Konfiguration des realen nginx/nftables-Setups, Firewall-Umgehung) | Kein reales Zielsystem/Netzwerk in dieser Sandbox verfügbar | | RDP/`guacd`-Pfad end-to-end (echtes Windows-Ziel, echtes `guacd`+FreeRDP) | Keine RDP-Zielumgebung verfügbar; Protokoll-Handshake wurde nur gegen die Spezifikation implementiert, nicht live gegenverifiziert | | SSH-Proxy end-to-end gegen ein echtes Linux-Ziel | Kein SSH-Zielsystem verfügbar; SSH-Verbindungslogik ist durch `asyncssh` (etabliert, nicht selbst geschrieben) abgedeckt, die serverseitige Anbindung (Key-Laden, Host-Key-Pinning) wurde isoliert/unit-artig, nicht end-to-end getestet | | Ansible-Rollen-Ausführung gegen ein reales Debian/Ubuntu-System | Kein Root-Zielsystem verfügbar; nur `--syntax-check` + YAML/Jinja-Parsing + Variablen-/Handler-Konsistenzprüfung möglich | | OpenSCAP/CIS-Benchmark-Tool-Lauf | Kein Zielsystem, auf dem `oscap` laufen könnte | | Social Engineering, physische Sicherheit | Nicht anwendbar auf ein Code-Review | | Lasttest / Denial-of-Service-Robustheit | Nicht Teil dieses Auftrags; separat zu betrachten | **Konsequenz:** Die hier bestätigte Sicherheit bezieht sich auf die Anwendungslogik selbst. Vor Produktivbetrieb sind ein echter Netzwerk-Pentest gegen die deployte Instanz und ein Live-Test des RDP-Pfads weiterhin erforderlich (siehe Abschnitt 4). --- ## 2. Ergebnisse: Statische Analyse ### 2.1 bandit (Python-Sicherheitslinter) | Vorher | Nachher | |---|---| | 1× Medium (SQL-String-Konstruktion, B608) | 0 | | 6× Low (breite `except`-Blöcke B110, subprocess-Nutzung B404/B603) | 0 (5× behoben durch Logging statt `pass`, 2× als geprüft/gerechtfertigt mit `# nosec` + Begründungskommentar markiert) | Alle Findings wurden behoben oder mit expliziter, im Code dokumentierter Begründung als bewusste Entscheidung markiert (kein stilles Wegklicken). Details: `app/auth/routes.py` (SQL-Konstruktion durch zwei feste parametrisierte Statements ersetzt), `app/security/av_scan.py`, `app/rdp_proxy/guacd_client.py`, `app/rdp_proxy/ws_tunnel.py`, `app/ssh_proxy/terminal_ws.py`. ### 2.2 pip-audit (Abhängigkeits-Schwachstellen) **16 bekannte Schwachstellen in 2 Paketen gefunden** (Stand vor Bereinigung): | Paket | Version (vorher) | Version (nachher) | Bekannte CVEs/Advisories | |---|---|---|---| | `cryptography` | 43.0.3 | **50.0.0** | 6 (u.a. PYSEC-2026-3553/3554, GHSA-537c-gmf6-5ccf) | | `starlette` | 0.46.2 | **1.6.0** | 10 (u.a. PYSEC-2026-1941/1942/2280/2281) | | `fastapi` | 0.115.x | **0.141.1** | (transitiv mit starlette aktualisiert) | | `pytest`, `pytest-asyncio` (Dev-only) | 8.3.5 / 0.24.0 | **9.1.1 / 1.4.0** | PYSEC-2026-1845 | Nach dem Upgrade: **0 bekannte Schwachstellen** in einem vollständigen `pip-audit`-Lauf über die gesamte aufgelöste Umgebung. `requirements.txt` und `requirements-dev.txt` sind jetzt exakt (lockfile-artig) auf die geprüften Versionen gepinnt. ⚠️ **Wichtiger Fund durch die Regressionstests:** Das Upgrade von Starlette 0.46 → 1.6 hat die Aufrufkonvention von `Jinja2Templates.TemplateResponse()` geändert (alte Signatur `TemplateResponse(name, {"request": request})` wurde entfernt, neue Signatur ist `TemplateResponse(request, name, context)`). Ohne die Pentest-Testsuite (`test_security_headers_present_on_every_response` u.a., die alle Seiten inkl. `/` aufrufen) wäre dieser **funktionale Regressionsbug** erst im Betrieb aufgefallen: `GET /`, `/dashboard`, `/terminal/{id}`, `/rdp/{id}` hätten alle mit HTTP 500 geantwortet. Behoben in `app/main.py`; alle vier Routen sind erneut per Test und manuellem `curl`-Aufruf verifiziert (Status 200, korrektes HTML). --- ## 3. Ergebnisse: Dynamischer Sicherheitstest (17 Testfälle) Alle 17 Testfälle sind **grün** (siehe `tests/test_pentest_security.py`, ausführbar mit `pytest tests/test_pentest_security.py -v`). Zusammenfassung nach Kategorie: | Kategorie | Geprüft | Ergebnis | |---|---|---| | **Authentifizierung** | Keine Username-Enumeration über Fehlermeldungen; Account-Lockout nach 5 Fehlversuchen (auch mit korrektem Passwort danach gesperrt); IP-basiertes Rate-Limiting greift bei >10 Versuchen/Minute; klassische SQLi-Payloads im Username-Feld führen weder zu 500 noch zu Auth-Bypass, Datenbank bleibt intakt | ✅ Bestanden | | **Session-/Cookie-Sicherheit** | Cookie trägt `HttpOnly`, `Secure`, `SameSite=Strict`; ein mit falschem Secret gefälschtes Cookie wird abgelehnt; Passwortänderung invalidiert alte Sessions (`session_version`-Mechanismus); "Überall abmelden" invalidiert das Cookie sofort | ✅ Bestanden | | **RBAC / IDOR** | Nicht-Admin kommt an keinen Admin-Endpunkt (weder lesend noch schreibend); Nutzer mit Rolle nur auf Hostgruppe A wird bei Host aus Hostgruppe B mit 403 blockiert — unabhängig davon, dass die Host-ID gültig/erratbar ist; eine Rolle für einen Aktionstyp (z.B. `ssh_connect`) gewährt **nicht automatisch** eine andere (z.B. `file_transfer`) auf demselben Host | ✅ Bestanden | | **Dateitransfer-Härtung** | Uploads über dem konfigurierten Limit werden mit 413 abgelehnt; als "infected" markierte Uploads werden blockiert (400) und erzeugen **keinen** Eintrag in `file_transfers` (kein falsches Erfolgssignal) | ✅ Bestanden | | **HTTP-Security-Header** | CSP, `X-Frame-Options: DENY`, `X-Content-Type-Options: nosniff`, `Strict-Transport-Security`, `Referrer-Policy` auf **jeder** Antwort inkl. Fehlerantworten; keine Stacktraces/Dateipfade in Fehlermeldungen | ✅ Bestanden | | **Audit-Log-Vollständigkeit** | Fehlgeschlagene und erfolgreiche Logins sowie TOTP-Enrollment erzeugen Audit-Einträge; Hash-Chain bleibt nach einem vollständigen Testlauf (viele parallele Nutzer, Fehlversuche, Admin-Aktionen) durchgängig intakt | ✅ Bestanden | ### 3.1 Bewertung nicht-automatisierter Beobachtungen (kein Fix nötig, dokumentiert) - **Pfadangabe bei SFTP-Dateitransfer** (`remote_path`-Parameter): erlaubt beliebige Pfade wie `../../etc/passwd`. Das ist **kein Jumphost-seitiges Path-Traversal**, da dieser Pfad ausschließlich an das **Zielsystem** per SFTP übergeben wird (keine lokale Dateisystem-Interaktion auf dem Jumphost selbst) — die Zugriffskontrolle liegt beim Zielsystem-Betriebssystem über den `ssh_username`. Funktional gewollt (Nutzer soll das Zielsystem wie mit einem SFTP-Client durchsuchen können). Empfehlung für sehr sensible Hostgruppen: zusätzliche pfadbasierte Allow-Lists auf Anwendungsebene als optionale Erweiterung (Konzept Kap. 12 könnte hierzu ergänzt werden). - **`hosts.address` ist nur admin-editierbar**, nicht durch normale Nutzer beeinflussbar — ein SSRF-artiger Angriffsvektor über beliebige Zieladressen ist daher kein nutzerseitig ausnutzbares Risiko, sondern liegt in der bestehenden Admin-Vertrauensgrenze (Admins könnten ohnehin beliebige RBAC-Rechte vergeben). --- ## 4. Offene Punkte vor Produktivbetrieb (Restrisiko) Diese Punkte waren bereits im ursprünglichen README als offen vermerkt und bleiben es, da sie eine echte Zielumgebung erfordern, die in dieser Sandbox nicht existiert: 1. **Netzwerk-Pentest gegen die tatsächlich deployte Instanz** (TLS-Konfiguration von nginx/direktem Uvicorn-TLS, Firewall-Regeln, tatsächliches Verhalten von fail2ban) — Black-Box-Test von außen. 2. **RDP-Pfad live gegen `guacd` + FreeRDP + Windows-Ziel testen** — insbesondere Parameter-Namen/-Reihenfolge der `connect`-Instruktion gegen die konkret eingesetzte guacd-Version verifizieren (siehe `app/rdp_proxy/guacd_client.py`, Docstring-Hinweis). 3. **`ansible-playbook --check` bzw. echter Rollout in einer Staging-Umgebung** — insbesondere die `pam_faillock`-Einbindung und die `/tmp`-/`/dev/shm`-Remount-Logik sind distributionsversionsabhängig und sollten vor der Produktivfreigabe an einem echten System bestätigt werden (siehe `ansible/roles/os_hardening/CIS_STIG_MAPPING.md`, Abschnitt „Bekannte Einschränkungen"). 4. **OpenSCAP-Lauf** gegen das gewählte CIS/STIG-Profil zur unabhängigen Bestätigung der Hardening-Rolle. 5. Alle bereits zuvor im README genannten funktionalen Erweiterungen (LDAP/AD, WebAuthn, verteiltes Rate-Limiting, CSRF-Token als zusätzliche Schicht) sind weiterhin nicht umgesetzt. ## 5. Fazit Die Anwendungslogik hat den durchgeführten Sicherheitstest ohne verbleibende offene Findings bestanden: keine Auth-/Session-Bypässe, keine RBAC-/IDOR- Lücken, keine SQL-Injection, vollständige Audit-Protokollierung, durchgängige Security-Header, saubere Abhängigkeiten (0 bekannte CVEs). Der Testlauf hat zusätzlich einen realen, durch das Dependency-Upgrade eingeführten Funktionsfehler gefunden und behoben — ein gutes Beispiel dafür, warum Sicherheitsupdates immer mit Regressionstests kombiniert werden sollten, nicht isoliert eingespielt werden dürfen. Die verbleibenden offenen Punkte sind ausschließlich solche, die eine echte Zielinfrastruktur voraussetzen und in einer isolierten Code-Sandbox grundsätzlich nicht abschließend geprüft werden können.