rebreak-monorepo/docs/specs/magic-re-hardening.md
chahinebrini 5fb441817f feat(magic): RE-hardening Quick Wins (ACL, #if DEBUG guards, rate-limit)
Härtung der öffentlich downloadbaren Magic-Apps gegen Reverse Engineering
(Assessment: docs/specs/magic-re-hardening.md):
- Windows: protection.json per ACL auf SYSTEM+Admins (DNS-Token nicht mehr von
  Standard-Usern lesbar) — setup.rs
- Mac: MacProfileInstaller.remove() + Debug-Supervision-Modi/Reset nur noch
  #if DEBUG (kein Removal-/Debug-Pfad im Release-Binary)
- Mac: staging-URL einmal als Konstante statt 4x Literal; interne Infra-Notizen
  aus String-Literalen raus
- Backend: Rate-Limit (10/IP/min) auf /api/magic/pair/redeem

NUR Backend-Teil deployt via Push; Mac/Win brauchen Xcode-/Cargo-Release-Build
(zied) + Smoke-Tests vor Release. MagicAPIClient.swift trägt etwas vorbestehenden
WIP mit (gleiche Magic-Client-Domäne).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 05:19:10 +02:00

252 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ReBreak Magic — RE-Hardening Assessment & Plan
**Erstellt:** 2026-06-09
**Scope:** `apps/rebreak-magic-mac/` · `apps/rebreak-magic-win/` · relevante Backend-Berührungspunkte
**Status:** Assessment + Plan zur Founder-Abnahme. Keine Implementierung.
---
## 1. RE-Exposure-Analyse: Was liegt aktuell offen?
### 1.1 Mac-App (SwiftUI, .app-Bundle)
**Technische Ausgangslage:** Swift-Binaries sind Mach-O, kompiliert mit Optimierungen. String-Literale im `__TEXT`-Segment bleiben im Binary. `strings`-Analyse + Class-Dump (mit `class-dump` oder Ghidra) reicht für alles Relevante.
| Angriffsvektor | Was ist extrahierbar? | Sicherheitskritisch? |
|---|---|---|
| String-Scan des Binary | `https://staging.rebreak.org`, `https://dns.rebreak.org/dns-query`, `org.rebreak.magic`, `org.rebreak.protection.dns.filter`, `org.rebreak.protection.profile` | Niedrig — Base-URLs sind keine Secrets |
| `REBREAK_BACKEND_URL` env-Override | Hardcoded String + Kommentar im Binary: „Default to staging (app.rebreak.org hat aktuell falsches TLS-Zert)" | Mittel — zeigt interne Infrastruktur-Anmerkungen |
| Keychain-Keys | `org.rebreak.magic` / `magic-session` — kein Secret-Wert, nur Service/Account-Namen | Niedrig |
| `mgc_`-Token-Prefix | Token-Format `mgc_<base64url>` im Binary erkennbar | Niedrig |
| Profil-PayloadIdentifier | `org.rebreak.protection.dns.filter.*` im Binary + `.mobileconfig`-Dateiname | Niedrig |
| `MacProfileInstaller.remove()` | Vollständiger Removal-Pfad via `profiles remove -identifier org.rebreak.protection.profile.*` sichtbar | **Mittel-hoch** — RE kann Removal-Kommando 1:1 nachbauen |
| `profiles show -type configuration` | Identifier-Scan-Logik offenbart die exakten Identifier des Schutzes | Mittel |
| Debug-State in `WizardModel` | `DebugSupervisionMode` mit `forceSupervised`/`forceUnsupervised` im Release-Build sichtbar | Mittel — gibt Hinweise auf interne States |
| Removal-Password | **NICHT** im Binary. Kommt server-seitig via `/api/magic/profile.mobileconfig`. Liegt in Keychain auf dem Device. | Kein Exposure-Risiko im Binary |
| AdGuard-Creds | **NICHT** im Binary. Nur im Backend via Infisical. | Kein Exposure-Risiko |
**Kritische Erkenntnis Mac:**
Das eigentliche Schutz-Bypass-Risiko liegt **nicht im Binary**, sondern im Mechanismus selbst: Der User hat Admin-Passwort. Mit Admin-Passwort kann er das .mobileconfig-Profil über System Settings → Geräteverwaltung entfernen — unabhängig vom Binary-Wissen. Das `ProfileRemovalPassword` ändert das erst dann, wenn es korrekt vom Backend injiziert wird. Das ist der echte Lock.
---
### 1.2 Windows-App (Tauri 2 / Rust)
**Technische Ausgangslage:** Rust-Binaries sind kompilierter Maschinencode. Ohne Debug-Symbols sind Funktionsnamen weg — aber String-Literale sind **direkt extrahierbar** mit `strings`. Tauri-Apps haben zusätzlich einen eingebetteten WebView mit React-Bundle (JavaScript-Klartext).
| Angriffsvektor | Was ist extrahierbar? | Sicherheitskritisch? |
|---|---|---|
| `strings` auf `.exe` | `https://staging.rebreak.org`, `dns.rebreak.org`, `178.105.101.137` (Fallback-IP), `RebreakProtection` (Service-Name), `HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters`, Registry-Pfade für Browser-Policies | Mittel |
| PowerShell-Scripts als String-Literale | **Komplett lesbar**`apply_script()`, `teardown_script()`, `check_script()` sind reine Strings im Binary. Jeder sieht exakt was geschrieben und gelöscht wird | **Hoch** — Angreifer kann Teardown-Sequenz 1:1 reproduzieren |
| Service-Name `RebreakProtection` | Im `sc.exe`-Aufruf als Literal; Service kann per Name gestoppt werden | **Hoch**`sc.exe stop RebreakProtection` + `sc.exe delete RebreakProtection` reicht für Bypass |
| DoH-Template `https://dns.rebreak.org/dns-query/{token}` | Token-Schema erkennbar | Niedrig |
| `protection.json` unter `%ProgramData%\ReBreak\` | Pfad im Binary. Datei enthält `dnsToken`, `serverIp`, `dohTemplate`, `backendUrl`, `deviceId` im Klartext | **Kritisch** — wer Pfad kennt, kann DNS-Token + Status-Polling-URL direkt lesen |
| Credential Manager Keys | `org.rebreak.magic` / `session-token` / `dns-token` — Keynames extrahierbar. Zugriff auf Werte braucht SYSTEM/selben User-Kontext | Mittel |
| WebView-Bundle (JS) | React-Frontend-Code im Klartext, komplett readable. Enthält keine Secrets (Token nie im Frontend), aber alle UI-Strings, State-Typen, IPC-Command-Namen | Niedrig — keine Secrets |
| `--console`-Debug-Flag | Im Binary sichtbar: `rebreak-protection-service.exe --console` startet den Service im Vordergrund | Niedrig — Debug-Feature, kein Angriffspfad |
| `DOH_FALLBACK_IP` `178.105.101.137` | Hetzner-Box direkt im Binary | Mittel — IP-Wissen; aber auch über DNS auflösbar |
**Kritische Erkenntnis Windows:**
**Das `protection.json`-File ist das größte Exposure-Problem.** Es liegt unter `%ProgramData%\ReBreak\protection.json` als lesbare JSON-Datei, enthält den DNS-Token im Klartext und ist für jeden User des Systems lesbar (ProgramData ist nicht ACL-beschränkt auf Nicht-Admin-User in Standardkonfiguration). Wer den Token kennt, kann `/api/magic/status?token=<token>` selbst pollen und damit theoretisch den Status-Check-Mechanismus verstehen.
**Der Service-Name-Bypass ist das kritischste operative Risiko:** Da `RebreakProtection` im Klartext im Binary steht und der Service mit `sc.exe` verwaltet wird, kann ein technisch versierter User mit Admin-Rechten:
1. `sc.exe stop RebreakProtection` ausführen
2. DoH manuell zurücksetzen (Registry/PowerShell)
3. Schutz ist weg — bis zum nächsten Service-Start (alle 5min Auto-Restart greift, aber Fenster ist offen)
Gegenargument: Das braucht Admin-Rechte. Jemand mit Admin kann ohnehin alles. Aber es macht es trivial und skriptbar.
---
### 1.3 Backend-Berührungspunkte
| Punkt | Exposure | Kritisch? |
|---|---|---|
| `/api/magic/status?token=` ist unauthentifiziert | Design-gewollt — Token ist das Secret. 48 hex chars = 192 bit Entropie → praktisch nicht brute-forcebar | Nein |
| Tamper-Service pollt diesen Endpoint | Jeder der den Token aus `protection.json` liest, kann denselben Request abschicken und sieht `active=true/false` | Theoretisch — kein zusätzlicher Bypass damit möglich |
| `profile.mobileconfig?token=` ist unauthentifiziert | Wie oben — Token-basierter Auth. Profil enthält RemovalPassword im Klartext (für macOS) | Mittel — Removal-PW liegt im Profil-XML falls jemand das Profil-File auf dem Mac lesen kann |
| AdGuard-Creds | Nur Backend / Infisical. Kein Client-Exposure | Kein Risiko |
| Pairing-Code 6 Ziffern (1M Raum) | 10min TTL, max 5 aktive Attempts unwahrscheinlich throttled | Niedrig — kurze TTL schützt |
---
## 2. Sicherheitsbewertung: Was ist tatsächlich kritisch?
### Kritisch (direkter Bypass möglich)
- **Windows `protection.json` lesbar:** DNS-Token im Klartext für lokale User
- **Windows Service-Stop via bekanntem Namen + Auto-Restart-Fenster:** 5min-Fenster zwischen Service-Stop und Restart (aber: braucht Admin)
### Mittel (kein direkter Bypass, aber schlechte Praxis / Competitor-Wissen)
- **PowerShell-Teardown-Script vollständig im Binary:** Competitor kann Setup/Teardown exakt kopieren
- **Mac `profiles remove` Kommando-Syntax im Binary:** Analog zum DNS-Profil-Removal
- **`protection.json`-Pfad bekannt:** Komfort-Angriff
### Niedrig (kein operativer Bypass)
- Base-URLs im Binary — ohnehin über Proxy/Network sichtbar
- Service/Keychain-Namen — braucht lokalen Zugriff
- Debug-Flags im Release-Build
### Kein Risiko
- Removal-Password (server-gehalten, nicht im Binary)
- AdGuard-Creds (Infisical-only)
- JWT/mgc-Tokens (Keychain/Credential Manager)
---
## 3. Härtungs-Plan (priorisiert)
### Priorität 1 — Quick Wins (13 Tage Aufwand)
#### 3.1 Windows `protection.json` ACL härten
**Problem:** Datei unter `%ProgramData%\ReBreak\` ist für Standard-User lesbar.
**Fix:** Im elevated Setup-Script ACL direkt nach dem Schreiben setzen:
```powershell
$acl = Get-Acl $state_path
$acl.SetAccessRuleProtection($true, $false)
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule("SYSTEM","FullControl","Allow")
$acl.SetAccessRule($rule)
$rule2 = New-Object System.Security.AccessControl.FileSystemAccessRule("Administrators","FullControl","Allow")
$acl.SetAccessRule($rule2)
# Standard-User: kein Zugriff
Set-Acl $state_path $acl
```
**Effekt:** DNS-Token nicht mehr über Datei extrahierbar ohne Admin-Rechte.
**Aufwand:** ~2h, nur `protection-core/src/lib.rs` + `setup.rs`-Script. Kein Backend-Change.
#### 3.2 Debug-Code aus Mac Release-Build entfernen
**Problem:** `DebugSupervisionMode` enum + Debug-Reset-Logik in `WizardModel.swift` ist im Release-Build sichtbar.
**Fix:** `#if DEBUG` Guards um alle Debug-Felder und `startDebugReset()`-Methode.
**Aufwand:** ~1h.
#### 3.3 Service-Name verschleiern (Windows)
**Problem:** `RebreakProtection` ist ein trivialer Stop-Angriff-Vektor.
**Diskussion:** Echter Schutz dagegen ist unmöglich — jeder Service-Name kann in der Service-Liste gesehen werden. Aber: einen weniger offensichtlichen Namen verwenden (z.B. `rbkdnsd` oder `SystemDnsOptimizer`) erhöht die Hürde für Script-Kiddies.
**Aufwand:** ~1h, aber Service-Name muss in protection-core, setup.rs, service/main.rs konsistent sein. + Re-Deploy.
**Warnung:** Security-Theater-Komponente. Echter Schutz: Windows Protected Services (nächste Priorität).
#### 3.4 Intern-Kommentar-Strings aus Mac-Binary entfernen
**Problem:** Kommentare wie „app.rebreak.org hat aktuell falsches TLS-Zert" sind als Swift-String-Literals im Binary.
**Fix:** Kommentare in Code-Kommentare umwandeln (nicht String-Literals), Infobox-Texte externalisieren.
**Aufwand:** ~30min.
---
### Priorität 2 — Mittlerer Aufwand (12 Wochen)
#### 3.5 Windows: Protected Service / PPL (Größter Tamper-Schutz)
**Problem:** `sc.exe stop RebreakProtection` mit Admin reicht zum Bypass.
**Lösung:** Windows Protected Processes / Service Protection Level. Via `sc.exe sdset` + DACL oder (besser) Service als **Protected Service** signieren — verhindert Stop/Delete auch durch lokale Admins.
**Realistisch:** Echter PPL braucht Microsoft ELAM-Zertifikat (teuer + aufwändig). Alternativer Ansatz: **DACL-Restriktion** — Setup-Script entfernt die WRITE/STOP-Berechtigung von der "Administrators"-Gruppe auf dem Service-Object. Zwingt Angreifer zu mehr Aufwand (Token-Impersonation o.ä.).
**Aufwand:** ~3 Tage (Recherche + Test auf verschiedenen Windows-Versionen). Hypothese, ungeprüft — Windows Service DACL-Manipulation braucht sorgfältiges Testing.
**Eskalation an rebreak-ops für Deploy, kein Backend-Change nötig.**
#### 3.6 Windows: PowerShell-Scripts verschlüsseln / nicht als String-Literal
**Problem:** Kompletter Teardown-Pfad im Binary.
**Lösung A (einfach):** Scripts zur Compile-Zeit mit einem festen XOR-Key oder AES-Key verschlüsseln, zur Laufzeit dekryptieren. Kein echter Schutz gegen entschlossenen Analysten, aber verhindert triviale `strings`-Analyse. **Security-Theater-Level ist hoch.**
**Lösung B (besser):** PowerShell-Scripts server-seitig halten. Client pollt bei Setup ein signiertes Script-Template vom Backend, führt es aus. Vorteil: Scripts updatebar ohne App-Release. Nachteil: Offline-Setup geht nicht.
**Empfehlung:** Lösung B für Teardown-Script (ohnehin server-getriggert), Lösung A für Setup (muss offline gehen).
**Aufwand:** Lösung A ~1 Tag; Lösung B ~3 Tage (+ Backend-Endpoint).
#### 3.7 Mac: Profil-Removal-Kommando nicht im Binary
**Problem:** `profiles remove -identifier org.rebreak.protection.profile.*` im Source.
**Kontext:** Diese Funktion wird bewusst für Testing/Reset verwendet (`MacProfileInstaller.remove()`). Im finalen User-Flow ist sie nicht mehr erreichbar. Trotzdem im Binary vorhanden.
**Lösung:** `remove()` Methode in `#if DEBUG` wrappen. Im Release-Build nicht kompilieren.
**Aufwand:** ~30min.
#### 3.8 Pairing-Code: Rate-Limiting auf Backend
**Problem:** 6-stelliger Code, 10min TTL. Aktuell kein explizites Rate-Limiting im `/api/magic/pair/redeem`-Endpoint sichtbar (IP-basiertes Rate-Limiting unklar).
**Fix:** Max. 10 Redeem-Attempts pro IP + 5 pro Code, danach 429. Im Nitro-Middleware oder Endpoint.
**Aufwand:** ~2h. Eskalation an rebreak-backend für generisches Rate-Limiting-Middleware falls noch nicht vorhanden.
#### 3.9 `protection.json` DNS-Token verschlüsseln (Windows)
**Problem:** DNS-Token liegt im Klartext in der JSON-Datei.
**Lösung:** DNS-Token mit einem Geräteschlüssel (DPAPI unter Windows — `ProtectedData.Protect` mit `DataProtectionScope.LocalMachine`) verschlüsseln vor Speichern in `protection.json`. DPAPI bindet an die Machine — entschlüsselbar nur auf demselben Gerät, nicht auf einem anderen PC.
**Effekt:** Datei-Exfiltration nützt nichts. Lokaler Admin-Zugriff für Entschlüsselung nötig (aber dann eh vorbei).
**Aufwand:** ~2 Tage. Rust DPAPI via `windows-sys` Crate, braucht Windows-only Conditional Compilation.
---
### Priorität 3 — Größerer Umbau / Nice-to-Have
#### 3.10 Binary-Obfuscation
**Mac (Swift):** Swift hat keine etablierten Open-Source-Obfuskatoren. ProGuard-Äquivalent existiert nicht. Möglichkeiten:
- Symbol-Stripping (ist Standard bei Release-Builds): reduziert Klassen/Methodennamen — bereits gut
- `swiftc` mit Optimierungen + `strip` — kein echter RE-Schutz
- Kommerzielle Tools (Obfusco, iXGuard) — teuer, fragile, selten Worth-it bei Swift
**Windows (Rust):** Rust-Binaries sind nach Release-Kompilierung bereits gut obfusziert (keine Klassen/Methoden-Namen ohne DWARF-Symbole). String-Literale bleiben das Problem (siehe 3.6). Packer wie `UPX` sind möglich aber:
- AV-Programme flaggen UPX-gepackte Binaries häufig
- Kein echter Security-Gain gegen entschlossenen Analysten
**Empfehlung:** Nicht investieren. ROI zu niedrig. Focus auf Secrets-Management + Service-Schutz.
#### 3.11 Certificate Pinning für Backend-Calls
**Aktuell:** Standard-TLS (kein Pinning). MITM-Angriff theoretisch möglich wenn User eigenes Root-Cert installiert.
**Mac:** `URLSession` mit Custom `URLSessionDelegate` + Pinning auf `staging.rebreak.org`-Cert / SPKI.
**Windows:** `reqwest` mit Custom Certificate Verifier.
**Aufwand:** ~3 Tage. Wartungsaufwand hoch (Cert-Rotationen). Relevant vor allem wenn App auf `app.rebreak.org` (Prod) umgestellt wird.
#### 3.12 Code-Signing Status
**Mac (aktuell unbekannt):** Notarization-Status des `.app`-Bundles ist unklar. Ohne Notarization zeigt macOS Gatekeeper-Warning beim ersten Start. **Zuständigkeit: User + zied (Developer-ID Credentials).**
**Windows (aktuell unbekannt):** Authenticode-Signatur des `.exe`. Ohne Signatur: SmartScreen-Warning bei Download/Start. Signatur mit EV Code Signing Cert (OV reicht nicht mehr für SmartScreen-Reputations-Aufbau). **Zuständigkeit: User + zied.**
**Diese Punkte sind keine RE-Härtung, aber essentiell für User-Trust und Download-Conversion.**
---
## 4. Zusammenfassung nach Plattform
### Mac
| # | Maßnahme | Aufwand | Priorität |
|---|---|---|---|
| 3.2 | Debug-Code aus Release entfernen | 1h | Quick Win |
| 3.4 | Intern-Strings/Kommentare bereinigen | 30min | Quick Win |
| 3.7 | `remove()` auf `#if DEBUG` | 30min | Mittel |
| 3.11 | Cert-Pinning | 3 Tage | Nice-to-have |
| 3.12 | Notarization (User/zied) | extern | Eskalation |
### Windows
| # | Maßnahme | Aufwand | Priorität |
|---|---|---|---|
| 3.1 | `protection.json` ACL härten | 2h | Quick Win |
| 3.3 | Service-Name weniger offensichtlich | 1h | Quick Win |
| 3.5 | Service DACL-Restriktion (Prüfen) | 3 Tage | Mittel |
| 3.6 | PS-Scripts nicht als Klartext-Strings | 13 Tage | Mittel |
| 3.9 | DNS-Token DPAPI-verschlüsseln | 2 Tage | Mittel |
| 3.12 | Authenticode-Signatur (User/zied) | extern | Eskalation |
### Backend
| # | Maßnahme | Aufwand | Priorität |
|---|---|---|---|
| 3.8 | Pairing-Code Rate-Limiting | 2h | Quick Win |
---
## 5. Was die Härtung NICHT schützt (Grenzen klar kommunizieren)
- **Admin-User kann alles aufheben.** Das ist kein Bug, das ist Windows und macOS. Der echte Schutz ist der Cooldown-Server + die Psychologie (24h reichen oft für den Impuls zu vergehen).
- **Binary-Obfuscation ≠ Schutz.** Ein entschlossener Competitor mit einem Analysten und einem Tag Zeit kommt an alles ran. Das Ziel ist: erhöhte Hürde für Gelegenheits-RE, nicht Unmöglichkeit.
- **Die Kern-Sicherheit liegt im Backend:** Token-Revocation ist server-seitig. Kein lokaler Angriff kann ein revoked Token reaktivieren. Das ist die richtige Architektur-Entscheidung und bleibt so.
---
## 6. Empfohlene Implementierungs-Reihenfolge
1. **Sofort (vor Public-Download):** 3.1 (ACL) + 3.2 (Debug-Guards) + 3.7 (Remove-Method) + 3.8 (Rate-Limiting) — zusammen ~1 Tag
2. **Sprint 1 (Woche 1):** 3.9 (DPAPI) + 3.6 Lösung A (Script-Obfuskation, keine echte Sicherheit aber reduziert Einblick)
3. **Sprint 2 (Woche 23):** 3.5 (Service-DACL, mit Test-Aufwand) + 3.11 (Cert-Pinning wenn auf Prod)
4. **Parallel, Founder/zied:** 3.12 Notarization + Authenticode — blockieren Download-Funnel ohne sie
---
## 7. Dateipfade für Implementierung
**Quick Wins betreffen:**
- `apps/rebreak-magic-win/src-tauri/protection-core/src/lib.rs` — ACL im Setup-Script-Generator, Service-Name
- `apps/rebreak-magic-win/src-tauri/src/setup.rs` — ACL-Kommando im build_setup_script
- `apps/rebreak-magic-mac/Sources/Models/WizardModel.swift`#if DEBUG Guards
- `apps/rebreak-magic-mac/Sources/Services/MacProfileInstaller.swift` — remove() in #if DEBUG
- `apps/rebreak-magic-mac/Sources/Services/MagicAPIClient.swift` — Kommentar-Strings
- `backend/server/api/magic/pair/redeem.post.ts` — Rate-Limiting