Härtung: REST-Query-Injection-Parität + TOFU-Fingerprint-Fallback

Dritter, sicherheitsfokussierter Deep-Dive-Durchgang ("maximale Sicherheit"):

- RestTransport.fetchFieldValues hatte dieselbe ungeschützte
  String-Interpolation wie das SSH-Pendant aus dem vorigen Fix, nur als
  URL-Query statt CLI-Zeile - beim ersten Fix übersehen. Jetzt
  RFC-3986-konform percent-encoded.
- CertificateFingerprint.sha256 fiel bei Extraktionsfehlern auf einen
  festen String "unbekannt" zurück statt echtem Fingerabdruck -
  theoretisches TOFU-Pinning-Bypass-Fenster (zwei verschiedene,
  extraktions-fehlschlagende Zertifikate hätten sich denselben
  "Fingerabdruck" geteilt). Rückgabetyp optional, Extraktionsfehler
  führt jetzt zu hartem Verbindungsabbruch statt Trust-Dialog.

Beide Fixes defensiv/gehärtet, nicht live exploitiert. Zugangsdaten-
Speicherung (Keychain) und BackupServices eigene Escaping-Logik
gegengeprüft - bereits korrekt. Build + alle 101 Unit-Tests grün.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Kay
2026-09-17 18:14:44 +02:00
co-authored by Claude Sonnet 5
parent 800b48406b
commit aad2ccb7d4
4 changed files with 100 additions and 4 deletions
+45
View File
@@ -192,6 +192,51 @@ Produktentscheidung ist, keine reine Korrektur. Nur als Beobachtung vermerkt.
---
## 2026-09-17 (Nachtrag 2: "finale Test für maximale Sicherheit")
Dritter Durchgang, gezielt sicherheitsfokussiert: Zugangsdaten-Speicherung (Keychain, SavedRoutersStore),
TOFU-Implementierung (Zertifikat/SSH-Hostkey), alle verbleibenden Stellen, die Text in Router-Befehle
oder URLs interpolieren. Positiv: `KeychainService` speichert Passwörter korrekt im macOS-Keychain
(nie Klartext/UserDefaults), `SavedRoutersStore`/`SavedRouter` enthalten kein Passwort-Feld,
`BackupService.escapeForRouterOSScript` escapte bereits vor diesem Durchgang korrekt (unabhängig von
Fund #5 — offenbar aus einem früheren Lockout-Vorfall gelernt), `SSHHostKeyFingerprint` hat keine
Fallback-Schwäche. Zwei weitere Lücken gefunden und gehärtet (defensiv, nicht live exploitiert):
### 8. REST-Pendant zu Fund #5: ungeschützte Query-String-Interpolation
**Status:** fixed (Build grün, nicht live exploitiert — aktuell nur mit hartkodiertem Wert aufgerufen)
**Confidence:** hoch (dieselbe Bugklasse wie #5, nur im REST- statt SSH-Pfad)
`RestTransport.fetchFieldValues` baute den Query-String `"\(restPath)?\(whereField)=\(whereValue)"`
ohne jede Kodierung — beim SSH-Gegenstück wurde das in Fund #5 bereits gefixt, das REST-Pendant
dabei übersehen. Ein `whereValue` mit `&` hätte einen zusätzlichen Query-Parameter einschleusen
können. Aktuell nur mit hartkodiertem `"no"` aufgerufen (kein Live-Exploit möglich), aber generische
`RouterOSTransport`-Methode — Härtung für zukünftige Aufrufer mit echtem Nutzer-/Gerätetext.
**Fix:** `whereValue` wird jetzt RFC-3986-"unreserved characters"-only percent-encoded (strenger
als `.urlQueryAllowed`, das `&`/`=`/`+`/`#` weiterhin durchlässt).
### 9. TOFU-Zertifikatsprüfung hatte einen Fallback-Konstante-Blindfleck
**Status:** fixed (Build grün, Härtung — kein realistisch auslösbarer Live-Exploit gefunden)
**Confidence:** mittel (theoretische Lücke im Code nachvollzogen, keine funktionierende PoC gebaut,
da eine echte TLS-Handshake-Situation gebraucht würde, in der `SecTrustCopyCertificateChain` trotz
abgeschlossenem Handshake leer zurückkommt — unüblich, aber laut Apple-API-Vertrag nicht ausgeschlossen)
`CertificateFingerprint.sha256(of:)` gab bei fehlgeschlagener Zertifikatsketten-Extraktion den festen
String `"unbekannt"` statt eines echten Fingerabdrucks zurück. Da `CertificateTrustStore.isTrusted`
Fingerabdrücke per exaktem String-Vergleich prüft, hätte das Vertrauen in EIN Zertifikat, dessen
Extraktion fehlschlägt, automatisch JEDES andere ebenfalls extraktions-fehlschlagende Zertifikat für
denselben Host als vertrauenswürdig durchgehen lassen — ein theoretisches TOFU-Pinning-Bypass-Fenster.
**Fix:** Rückgabetyp auf `String?` geändert; schlägt die Extraktion fehl, lehnt
`RestTransport.urlSession(_:didReceive:completionHandler:)` die Challenge jetzt hart ab
(`.cancelAuthenticationChallenge`, kein `lastRejectedFingerprint` gesetzt) — der Nutzer bekommt in
diesem Fall nie einen "Zertifikat vertrauen?"-Dialog mit einer nicht verifizierbaren Kennung
angeboten, sondern einen normalen Verbindungsfehler.
Build + alle 101 Unit-Tests grün nach beiden Fixes.
---
## Noch nicht geprüft / außerhalb dieses Durchgangs
- UI-Interaktion selbst (Klickpfade, Darstellung) — nicht automatisierbar, siehe Hinweis oben.