Ergebnis einer beauftragten Sicherheitsbegutachtung. Die WebAuthn- Zeremonie selbst ist nicht angefasst worden - sie war in Ordnung. Alle Befunde lagen DANEBEN: Ein zweiter Faktor ist nur so stark wie der schwaechste Weg zum Konto, und Sitzungen, die aus ihm entstehen, erben seinen Schutz nicht. KRITISCH /api/setup/v1/login prueste auf totp_secret - eine Spalte, die kein Anmeldeweg auswertet - und nicht auf dc_user_credentials. Ein Administratorpasswort genuegte dort fuer ein setup:install-Token ohne Hardwarekey. Neu: Konten mit zweitem Faktor abgewiesen, und die Rollentrennung gilt symmetrisch. Der Docblock behauptete sie seit jeher; erzwungen hat sie nichts. HOCH secondFactorRequired(), accountLockoutSeconds() und lockoutSeconds() fingen jedes Throwable und fielen auf "kein Faktor" bzw. "nicht gesperrt" zurueck. Gefangen wurden damit auch Verbindungsabbrueche und Lock-Timeouts - jeder davon schaltete still fuer alle Konten auf Passwort-allein. Toleriert werden jetzt nur noch 42S02 und 42S22. HOCH Fuenf Aktionen aenderten die Anmeldemittel ohne erneute Bestaetigung. Eine uebernommene Sitzung konnte sich damit dauerhaft einnisten. Neu: Authentifizierung aus den letzten zehn Minuten noetig, geprueft an derselben Stelle, an der auch der Leerlauf sperrt. WEITER - Leerlauf 30 statt 120 Minuten, und GESPERRT statt beendet: AAL2 laesst zur Reaktivierung einen Faktor genuegen. Wer alle 30 Minuten den Hardwarekey vorzeigen muss, laesst ihn stecken. - Migration 022: session_epoch. Ein Passwortwechsel beendet die uebrigen Sitzungen des Kontos, die eigene bleibt. - Kontosperre wirkt auf der zweiten Stufe; Zaehler gegen unbegrenztes Raten im Zwischenzustand. - Drosselung auf login.php (10/60s) und /api/auth/v1/ (30/60s). - Anmeldefehler ununterscheidbar; die IP-Sperre darf benannt bleiben, sie sagt nichts ueber ein Konto. - HSTS, TLS-Zwang VOR allen Routen, CSP. Die CSP traegt wegen 93 Inline-Handlern nicht gegen XSS - das steht so im Kommentar. - session.use_strict_mode, CSRF-Token bei der Anmeldung verworfen, Secure-Flag aus app.url statt aus $_SERVER['HTTPS']. - rpId/expectedOrigin nicht mehr aus dem Host-Header. /api/health meldet unter checks.webauthn_anchor, ob der Anker steht. - Notfalltoken laeuft nach 180 Tagen ab, geprueft nach dem Hashvergleich. - config.example.php ohne echtes Passwort, verify_ssl auf true. - Serverdaten.txt.bak mitgesperrt, dc_login_attempts raeumt auf. BELEGT 188 von 188 Testfaellen gruen, darunter drei neue in der Gruppe "haertung". Die Blockade einer veralteten Sitzung liess sich nicht in der Suite pruefen - sie verlangt, das Zehn-Minuten-Fenster wirklich abzuwarten. Dafuer gibt es ein Werkzeug ausserhalb; es belegt, dass der Formularweg auf den Sperrbildschirm fuehrt, die Regel dabei unveraendert bleibt, der JSON-Weg reauth_required meldet und das Passwort allein wieder oeffnet. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
37 lines
1.9 KiB
SQL
37 lines
1.9 KiB
SQL
-- Migration 022: Sitzungsgeneration je Konto
|
|
--
|
|
-- Additive Migration. Der Migrator toleriert 1050/1060/1061/1062/1091.
|
|
--
|
|
-- ---------------------------------------------------------------------------
|
|
-- Wozu
|
|
-- ---------------------------------------------------------------------------
|
|
-- Ein Passwortwechsel beendete bisher keine einzige Sitzung. Wer das Passwort
|
|
-- aendert, weil er einen fremden Zugriff vermutet, liess damit genau den
|
|
-- drin, dessentwegen er es aendert: Dessen Cookie galt unveraendert weiter.
|
|
--
|
|
-- PHP legt Sitzungen als Dateien ab; es gibt keinen Index "alle Sitzungen des
|
|
-- Kontos X", ohne einen eigenen Sitzungsspeicher zu bauen. Eine Generation
|
|
-- loest dasselbe Problem mit einer Spalte: Jede Sitzung merkt sich beim
|
|
-- Anmelden den Stand, und bei jeder Anfrage wird verglichen. Wird der Stand
|
|
-- erhoeht, passen alle uebrigen Sitzungen nicht mehr - ohne dass man sie
|
|
-- einzeln finden muesste.
|
|
--
|
|
-- Die eigene Sitzung uebernimmt den neuen Stand sofort. Wer sein Passwort
|
|
-- aendert, soll nicht sich selbst hinauswerfen; das waere die Art von
|
|
-- Nebenwirkung, wegen der Leute Passwoerter nicht aendern.
|
|
--
|
|
-- ---------------------------------------------------------------------------
|
|
-- Warum Vorgabe 0 und nicht ein Zeitstempel
|
|
-- ---------------------------------------------------------------------------
|
|
-- Ein Zaehler ist gegenueber einer Uhr im Vorteil: Er braucht keine
|
|
-- Zeitzonen, keine Sekundengenauigkeit und kein Nachdenken darueber, was
|
|
-- passiert, wenn zwei Aenderungen in dieselbe Sekunde fallen. Verglichen wird
|
|
-- auf Gleichheit, nicht auf "juenger als".
|
|
--
|
|
-- Sitzungen, die vor dieser Migration eroeffnet wurden, tragen den Wert gar
|
|
-- nicht. Sie gelten weiter (siehe Auth::sessionEpochValid): Eine
|
|
-- Sicherheitsverbesserung, die im Moment ihrer Einfuehrung alle Angemeldeten
|
|
-- hinauswirft, wird beim naechsten Mal abgeschaltet.
|
|
ALTER TABLE dc_users
|
|
ADD COLUMN session_epoch INT NOT NULL DEFAULT 0 AFTER recovery_used_at;
|