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>
25 lines
1.2 KiB
Swift
25 lines
1.2 KiB
Swift
import Foundation
|
|
import Security
|
|
import CryptoKit
|
|
|
|
enum CertificateFingerprint {
|
|
/// `nil` means the certificate chain couldn't be read at all — the caller must treat that as
|
|
/// a hard connection failure, never as a fingerprint to compare or offer for trust. The
|
|
/// previous version returned the fixed string `"unbekannt"` in that case, which — however
|
|
/// unlikely to trigger in practice — meant every certificate that failed extraction shared
|
|
/// the exact same "fingerprint", so trusting one such (extraction-failed) certificate would
|
|
/// have silently trusted every other one too, defeating TOFU pinning for that host. bugs.md,
|
|
/// "maximale Sicherheit"-Durchgang, 2026-09-17 — hardening, not a live-reproduced exploit.
|
|
static func sha256(of trust: SecTrust) -> String? {
|
|
guard
|
|
let chain = SecTrustCopyCertificateChain(trust) as? [SecCertificate],
|
|
let leaf = chain.first
|
|
else {
|
|
return nil
|
|
}
|
|
let data = SecCertificateCopyData(leaf) as Data
|
|
let digest = SHA256.hash(data: data)
|
|
return digest.map { String(format: "%02X", $0) }.joined(separator: ":")
|
|
}
|
|
}
|