1
0
Files
breadcrumb-the-shire/docs/explanation-session-management.md
fs 0e86925464 refactor: rename lib/ to core/ for clearer core-module separation
Rename the top-level lib/ directory to core/ so the project root
immediately communicates which code is core platform and which lives
in modules/. PHP namespaces (MintyPHP\*) are unchanged — only the
Composer PSR-4 path mapping moves from lib/ to core/.

Module-internal lib/ directories (modules/*/lib/) are untouched.

Updated across the full stack:
- composer.json PSR-4 mapping
- bootstrap entry points (web/index.php, bin/*, tests/bootstrap.php)
- tooling configs (phpstan.neon, phpunit.xml, php-cs-fixer)
- 26 architecture contract tests
- enforcement-policy, guard-catalog, quality-gates
- all documentation (CLAUDE.md, README, 25 docs/, .agents/skills/)
- bin/qa-extended.sh search paths
- .claude/settings.local.json permission paths

Workflow: .agents/runs/CORE-LIB-RENAME-001/ (Analyst → Planner →
Executor → Code Review (4 findings fixed) → Security Review →
Acceptance Test → Finalizer)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 23:20:42 +02:00

6.5 KiB
Raw Blame History

Session-Management

Letzte Aktualisierung: 2026-03-14

Ziel

Vollstaendige Erklaerung des Session-Lifecycle: Wann wird ein User abgemeldet, wann greift der Remember-Token, wie bleiben Frontend und Server synchron.

Session-Lifecycle (Uebersicht)

Login (Passwort / SSO)
  │
  ├─ $_SESSION['session_started_at']   = time()
  ├─ $_SESSION['session_last_activity'] = time()
  └─ Optional: Remember-Token (Cookie, 30 Tage)
  │
  ▼
Jeder Request (web/index.php)
  │
  ├─ Session vorhanden?
  │   ├─ JA  → Idle-Check + Absolute-Check
  │   └─ NEIN → Remember-Cookie vorhanden?
  │              ├─ JA  → Auto-Login (Token validieren + rotieren)
  │              └─ NEIN → Redirect /login
  │
  ├─ authz_version geaendert? → Permissions neu laden
  └─ User deaktiviert/geloescht? → Forced Logout

Zwei Timeouts

Timeout Default Bereich Zuruecksetzbar? DB-Key
Idle 30 Min 5 Min 24 Std Ja, bei jeder Aktivitaet session_idle_timeout_minutes
Absolute 8 Std 1 Std 72 Std Nein, nie session_absolute_timeout_hours

Pruefung in web/index.php (bei jedem Request):

Idle:     (now - last_activity)    > idle_timeout     → Timeout
Absolute: (now - session_started_at) > absolute_timeout → Timeout

Einer greift → logoutDueToSessionTimeout() → Audit-Event auth.session.timeout → Flash-Message → Redirect /login?return_to=<url>.

Wichtig: Der Absolute-Timeout wird nie zurueckgesetzt, auch nicht durch "Session verlaengern". Er begrenzt die maximale Sitzungsdauer hart.

Frontend-Warning und Keepalive

Quelle: web/js/components/app-session-warning.js

0 Min           28 Min          30 Min
├───────────────┤───────────────┤
  Aktiv           Warning-Dialog   Timeout
                  (2 Min Countdown)

Interaktions-Tracking

  • Events: click, keydown, scroll, mousemove, touchstart
  • Throttled auf 30s (kein Overhead bei schnellem Scrollen)
  • Setzt nur den JS-Timer zurueck, nicht den Server-Timer

Background-Keepalive

Problem ohne Keepalive: User scrollt 29 Min auf einer Seite → JS zeigt "aktiv", Server sagt "idle".

Loesung: Alle 5 Minuten prueft ein Intervall ob seit dem letzten Sync Interaktion stattfand. Falls ja → POST /admin/session-ping/data → Server-Timer zurueckgesetzt.

Szenario Keepalive-Ping?
User aktiv (Maus, Scroll, Klick) Ja, alle 5 Min
User wirklich idle Nein
Warning-Dialog offen Nein
Tab im Hintergrund Nein (keine Events)

Warning-Dialog

  • Erscheint 2 Min vor Idle-Timeout
  • Nur bei Idle-Timeout >= 3 Min
  • Kann nicht per ESC oder Backdrop geschlossen werden
  • Zwei Optionen: Session verlaengern (POST Ping) oder Abmelden (Redirect /logout)

Remember-Token

Quelle: core/Service/Auth/RememberMeService.php

Token-Struktur

Cookie "remember" = selector:token
                    (24 hex) (64 hex)

DB: user_remember_tokens
  ├─ selector      → Lookup (UNIQUE Index)
  ├─ token_hash    → SHA-256 (constant-time Verify)
  ├─ expires_at    → Default 30 Tage (konfigurierbar 1-365)
  └─ last_used

Selector und Token sind getrennt: Selector fuer schnellen DB-Lookup, Token-Hash fuer timing-sichere Validierung. Kein Klartext-Token in der DB.

Auto-Login-Ablauf

  1. Kein aktiver Session-User → Remember-Cookie lesen
  2. Rate-Limit pruefen (IP nicht blockiert?)
  3. Selector → DB-Lookup
  4. Token → hash_equals(SHA256) Verify
  5. User aktiv? Token nicht abgelaufen?
  6. Session neu aufbauen (User, Permissions, Tenant-Kontext)
  7. Token rotieren (neuer Hash, neues Expiry)
  8. Rate-Limit-Counter zuruecksetzen

Token-Rotation bei jedem Auto-Login begrenzt das Fenster bei Cookie-Diebstahl.

Rate Limiting (Remember-Token)

Quelle: core/Service/Auth/RememberMeRateLimiter.php

  • Storage: Memcached (Cache::add + Cache::increment)
  • Limit: 5 Fehlversuche pro IP in 15 Minuten
  • Counter laeuft automatisch ab (Memcached TTL)
  • Fail-open: Bei Memcached-Ausfall wird nicht blockiert
  • Erfolgreicher Auto-Login setzt Counter zurueck

Siehe /docs/howto-anfragelimits.md fuer alle Rate-Limits.

Zusammenspiel Absolute Timeout + Remember-Token

Login → 8h Absolute Timeout → Session endet
                                  │
                           Remember-Token?
                           ├─ JA  → Neue Session (neuer 8h-Timer!)
                           └─ NEIN → Login-Seite

Solange der Remember-Token lebt (Default 30 Tage), bekommt der User nach Ablauf des Absolute-Timeout automatisch eine neue Session. Der Uebergang ist nahtlos (kein Flash, kein Login-Screen).

Das bedeutet: Mit aktivem Remember-Token ist die effektive Session-Dauer nicht 8 Stunden, sondern bis zu 30 Tage. Das ist by-design, sollte aber bei Compliance-Anforderungen (z. B. echte Re-Authentifizierung nach X Stunden) beruecksichtigt werden.

Attribut Wert Grund
httponly true Kein JS-Zugriff (XSS-Schutz)
samesite Lax CSRF-Schutz bei Cross-Site-Navigation
secure dynamisch true bei HTTPS, false bei HTTP (Dev)
path / Gueltig fuer gesamte Domain

Das Secure-Flag wird automatisch anhand des Protokolls gesetzt (RequestRuntime::isSecure()). In Produktion muss HTTPS ueber Nginx erzwungen werden (Redirect 80→443 + HSTS).

Audit-Events

Event Ausloeser
auth.login.success Erfolgreicher Login (Passwort/SSO)
auth.login.failed Fehlgeschlagener Login
auth.logout Manueller Logout
auth.session.timeout Idle- oder Absolute-Timeout

Beteiligte Dateien

Komponente Pfad
Timeout-Pruefung web/index.php
AuthService core/Service/Auth/AuthService.php
RememberMeService core/Service/Auth/RememberMeService.php
RememberMeRateLimiter core/Service/Auth/RememberMeRateLimiter.php
Session-Settings core/Service/Settings/SettingsSessionGateway.php
Frontend-Warning web/js/components/app-session-warning.js
Session-Ping pages/admin/session-ping/data().php
Login-Action pages/auth/login().php
SSO-Callback pages/auth/microsoft/callback().php

Vertiefung

  • /docs/explanation-sicherheitsmodell.md
  • /docs/howto-anfragelimits.md
  • /docs/howto-auth-sso-smoke-test.md
  • /docs/reference-konfiguration.md