Ziel: MFA-Pflicht für Admin-Logins, damit MH-5b/c/d Mutation-Actions (Approve / Reject / Pause / Resume / Release) sicher freigeschaltet werden können.
Vorbedingung: SEC-1b-0/1/2/3/4/5 abgeschlossen, MH-5a (Read-only ManagedProposalResource) live.
| Item | Value |
|---|---|
| master HEAD | a185182 |
git status | clean (außer Container-Rename-Reste, DEFERRED) |
GUI HTTP /admin/login | (Reverse-Proxy-Übergang gerade aktiv — ungefährlich) |
APP_URL | http://127.0.0.1:8090 — PLAIN HTTP |
SESSION_SECURE_COOKIE | nicht gesetzt |
| Bot in-container PID | unverändert |
| Worker in-container PID | unverändert |
BINANCE_TESTNET | true |
| Mainnet | 5-layer block ✓ |
managed_proposals rows | 0 |
| Installierte MFA-Bausteine | pragmarx/google2fa 9.0.0 + pragmarx/google2fa-qrcode 3.0.0 ✓ |
| Filament-native MFA | vendor/filament/filament/src/Auth/MultiFactor/{App,Email,Contracts,Http,Pages} ✓ |
Major Finding: Filament 5 hat native TOTP-MFA + native Email-MFA out-of-the-box. Pakete pragmarx/google2fa{,-qrcode} sind bereits installiert (transitive aus filament/filament). Kein zusätzliches Composer-Paket nötig für Option A.
Blocker-Finding: APP_URL=http://… → die GUI läuft aktuell ohne HTTPS. WebAuthn/Passkey funktioniert nur in einem „secure context“ (HTTPS mit gültigem Zertifikat) — siehe SEC-1a H5 (self-signed cert). Browser akzeptieren Passkey-Enrollment praktisch nicht über plain HTTP und nur unzuverlässig über self-signed HTTPS.
app_authentication_secret (encrypted text)app_authentication_recovery_codes (encrypted JSON-Array, 8 Codes by default)User implementiert HasAppAuthentication + HasAppAuthenticationRecovery:
public function getAppAuthenticationSecret(): ?string;
public function saveAppAuthenticationSecret(?string $secret): void;
public function getAppAuthenticationHolderName(): string; // → email
public function getAppAuthenticationRecoveryCodes(): ?array;
public function saveAppAuthenticationRecoveryCodes(?array $codes): void;
AdminPanelProvider:
->multiFactorAuthentication([
AppAuthentication::make()->recoverable(),
])
->requiresMultiFactorAuthentication() // Pflicht für alle Admins
app_authentication_secret braucht App-Encryption (Laravel encrypted cast); kompromittierte APP_KEY → MFA-Bruch.users).User-Modell: 5 Methoden + 2 casts.AdminPanelProvider: 2 Zeilen.| Paket | Filament-Version | Mainline-Stand | Notiz |
|---|---|---|---|
jeffersongoncalves/filament-multifactor-passkeys | „Filament 5 multi-factor“ | unbekannt | Klingt am nächsten am Ziel — Audit nötig |
marcelweidum/filament-passkeys | unbekannt | Community | |
statview/filament-passkeys | unbekannt | Community | |
Eigenbau auf web-auth/webauthn-lib + Filament-Hooks | n/a | mature lib | volle Kontrolle, hoher Aufwand |
Keiner dieser Plugins ist offiziell von Filament gewartet. Filament 5 hat kein natives Passkey-Modul (nur App-TOTP + Email).
webauthn_credentials Tabelle (1:n: credential_id, public_key, sign_count, transports, attestation, created_at).navigator.credentials.create(…) mit Server-Challenge.navigator.credentials.get(…).https:// (oder localhost). Aktuelle APP_URL=http://127.0.0.1:8090 ist Showstopper. SEC-1a H5 (self-signed cert / Let's Encrypt-Setup) muss zuerst gefixt sein.127.0.0.1 mit Port geht nur via localhost-Ausnahme, kein remote-Browser-Setup.web-auth/webauthn-lib ist ~1200 Klassen, neues Audit-Oberflächengebiet.webauthn_credentials).| Kriterium | Option A: TOTP | Option B: Passkey/WebAuthn |
|---|---|---|
| Sicherheits-Niveau | Mittel (HOTP/TOTP-Standard RFC 6238) | Hoch (FIDO2, origin-bound) |
| Phishing-Resistenz | ✗ replay-fähig im 30s-Window | ✓ origin-bound, nicht phishable |
| Implementierungs-Aufwand | ✓ 2–3h (Filament-native) | ✗ 12–20h (inkl. HTTPS) |
| Lockout-Risiko | Niedrig (Recovery-Codes built-in) | Mittel (eigener Recovery-Flow nötig) |
| Rollback-Risiko | ✓ rein additive Migration | Mittel (neue Tabelle, JS-Hooks) |
| Testbarkeit | ✓ Google2FA deterministisch | ⚠ Browser-API, schwerer zu testen |
| HTTPS-Vorbedingung | ✓ keine | HARTE Pflicht (Blocker H5) |
| Recovery / UX | Recovery-Codes + Re-Setup | Recovery-Codes selbst implementieren |
| Audit-Surface | Klein (Filament-internes Modul) | Groß (Community-Plugin oder webauthn-lib) |
| Operator-UX | Authenticator-App, ~10s Code | 1-Tap Biometrie (wenn Setup okay) |
| Composer-Pakete neu | 0 | 1–2 (Plugin + ggf. lib) |
| Roadmap-Wert | Kurzfristig pragmatisch | Strategisch + premium |
| Phasen-Risiko (MH-5b/c/d Block) | Niedrig | Hoch |
multiFactorAuthentication([AppAuthentication::make(), PasskeyAuthentication::make()]).| Req-ID | Anforderung | erfüllt von |
|---|---|---|
| MFA-R1 | MFA Pflicht für alle UserRole::Admin | requiresMultiFactorAuthentication() im AdminPanelProvider |
| MFA-R2 | Kein Bypass via php artisan tinker / Eloquent-Direkt-Edit (außer Notfall-Reset durch DB-Admin) | dokumentiert + audit-log auf reset-event (BACKLOG) |
| MFA-R3 | Secrets niemals in Logs / Telegram / PDF / GUI | encrypted column + nie in dd()/Log::info() |
| MFA-R4 | Recovery-Codes einmalig nutzbar, encrypted at rest | Filament-default ✓ |
| MFA-R5 | Lockout-Pfad dokumentiert (Operator-Runbook) | SOP: DB-direkt-NULL der app_authentication_secret-Spalte mit Audit-Log |
| MFA-R6 | MFA-Flow respektiert MAX_LOGIN_ATTEMPTS=3 (SEC-1b-5) | Throttle bleibt auf 1st-Step; 2nd-Step bekommt eigenen Throttle |
| MFA-R7 | MFA-Setup-Page nur post-Login, nie public | Filament-default (Profil-Page ist auth-required) ✓ |
| MFA-R8 | TESTNET-Banner / Mainnet-Block bleiben ungetouched | reine GUI-Auth-Layer-Änderung |
| MFA-R9 | Test-Coverage: Setup / Login-Step / Recovery-Code / Failed-Code / Disabled-User | ≥10 PHPUnit-Tests via Safe-Runner |
| MFA-R10 | Keine Bot-Code- / Worker- / Watchdog-Berührung | rein GUI-Layer ✓ |
| MFA-R11 | Backup vor Migration (durable rule: pg_dump + state-snapshot) | Pflicht-Schritt im Implementations-Plan |
| MFA-R12 | Restart-Anforderung dokumentiert (web-server reload reicht; kein Bot/Worker) | klar abgegrenzt |
Variante D — Staged (TOTP-first jetzt, Passkey post-HTTPS später).
pragmarx/google2fa{,-qrcode} reduzieren Implementierungs-Risiko auf nahezu Null.APP_URL=http://127.0.0.1:8090 ist heute Showstopper. Diese Infrastruktur-Vorbedingung (Let's Encrypt + Domain) gehört in eine eigene SEC-1c-Phase, nicht in SEC-1b-6.MAX_LOGIN_ATTEMPTS=3 + fail2ban + 169-IP-Banlist akzeptabel, solange Mainnet hard-blocked bleibt.| Phase | Inhalt | Block für |
|---|---|---|
| SEC-1b-6 (Variante D Step 1) | TOTP-MFA Filament-native, 2 DB-cols, Recovery-Codes, ~10 Tests | unblockt MH-5b/c/d |
| MH-5b | Request + Reject Actions | nach SEC-1b-6 |
| MH-5c | Approve-Wizard 5 Steps | nach SEC-1b-6 |
| MH-5d | Pause/Resume/Release Actions | nach SEC-1b-6 |
| SEC-1c | DB-Privs / Let's Encrypt / Admin / Session-Encrypt | parallel zu MH-5x erlaubt |
| SEC-1d (BACKLOG) | Passkey/WebAuthn als zusätzlicher Faktor (Variante D Step 2) | nach SEC-1c HTTPS + Domain |
| MH-6 | Worker-Handler für 8 CommandTypes | nach MH-5 + SEC-1b-6 |
| MH-7 | Bot-Wiring (Restart!) | nach SEC-1c |
Geliefert würde (im späteren Implementations-Patch, nicht jetzt):
users add 2 columns:
app_authentication_secret (string, encrypted, nullable)app_authentication_recovery_codes (text, encrypted, nullable)User-Modell implementiert HasAppAuthentication + HasAppAuthenticationRecovery; 2 encrypted casts.AdminPanelProvider:
->multiFactorAuthentication([
\Filament\Auth\MultiFactor\App\AppAuthentication::make()->recoverable(),
])
->requiresMultiFactorAuthentication()
Login-Page: keine Änderung. 2nd-step wird von Filament automatisch eingehängt.docs/ops/sec_1b_6_mfa_runbook.md mit Setup-Flow, Recovery-Code-Storage-Empfehlung, Lockout-Reset-SOP.Log::* schreibt nie das Secret.tradingbot_gui + .env + state-snapshot VOR Migration..env snap, live_portfolio.json snap, Bot-PID-stamp, fail2ban-status-snap.down() möglich.docker cp notwendig (Migration läuft via php artisan migrate im GUI-Container).Bevor SEC-1b-6 Code-Phase startet, GO/NO-GO erforderlich auf: