129 lines
6.5 KiB
Markdown
129 lines
6.5 KiB
Markdown
|
|
# Sicherheitsmodell
|
||
|
|
|
||
|
|
Letzte Aktualisierung: 2026-02-21
|
||
|
|
|
||
|
|
## 1) Grundprinzip
|
||
|
|
|
||
|
|
Die Anwendung kombiniert mehrere Sicherheitslayer:
|
||
|
|
|
||
|
|
1. Request-Grenze (Nginx + Minty Firewall)
|
||
|
|
2. Session/Auth-Guard
|
||
|
|
3. Permission-Checks
|
||
|
|
4. Tenant-Scope-Checks
|
||
|
|
5. Endpoint-spezifische Schutzlogik (z. B. Avatar-Dateien, Exporte, Actions)
|
||
|
|
|
||
|
|
Kein einzelner Layer ist ausreichend; die Kombination ist der Schutz.
|
||
|
|
|
||
|
|
## 2) Authentifizierung
|
||
|
|
|
||
|
|
- Session-basierter Login.
|
||
|
|
- Optionales Remember-Me über `user_remember_tokens`.
|
||
|
|
- Auto-Login aus Cookie erfolgt früh in `/web/index.php`.
|
||
|
|
- Zentraler Login-Entry ist `login` (optional `login?tenant=<slug>` für Tenant-Vorauswahl).
|
||
|
|
- Unknown-Email-Verhalten im Login bleibt generisch (keine Tenant-/Account-Details vor erfolgreicher Auflösung).
|
||
|
|
|
||
|
|
Kritisch:
|
||
|
|
|
||
|
|
- Tokens sind gehasht gespeichert.
|
||
|
|
- Tokens können zentral ablaufen gelassen werden (Settings-Gefahrenzone).
|
||
|
|
|
||
|
|
## 3) Public vs. Protected Routes
|
||
|
|
|
||
|
|
- Public Routes werden in `/config/routes.php` mit `public => true` definiert.
|
||
|
|
- Daraus wird zur Laufzeit `APP_PUBLIC_PATHS` aufgebaut.
|
||
|
|
- Alles andere ist loginpflichtig.
|
||
|
|
|
||
|
|
## 4) Permission-System
|
||
|
|
|
||
|
|
- Berechtigungen sind feingranular (`permissions` + `role_permissions`).
|
||
|
|
- Rollen vergeben Berechtigungen an User (`user_roles`).
|
||
|
|
- Actions prüfen Berechtigungen explizit.
|
||
|
|
|
||
|
|
Empfehlung:
|
||
|
|
|
||
|
|
- Rechte für View/Edit/Delete getrennt halten.
|
||
|
|
- Admin-only Endpunkte immer serverseitig absichern, nicht nur im UI verstecken.
|
||
|
|
- Onboarding-PDFs für Benutzer laufen über eigenes Recht `users.access_pdf` (Single + Bulk).
|
||
|
|
- Tenant-Zusatzfelder sind getrennt abgesichert:
|
||
|
|
- `custom_fields.manage` für Definitionen/Optionen im Tenant
|
||
|
|
- `custom_fields.edit_values` für Wertepflege am User.
|
||
|
|
- `field_key` wird serverseitig erzeugt/stabil gehalten (kein editierbares UI-Feld), damit tenant-weite Eindeutigkeit erhalten bleibt.
|
||
|
|
- Theme-Wechsel für User wird tenant-spezifisch abgesichert:
|
||
|
|
- effektive Regel kommt aus `current_tenant.allow_user_theme` (Fallback global `app_theme_user`)
|
||
|
|
- Endpoint `admin/users/theme` blockt bei deaktivierter Regel mit `403` (`theme_disabled`).
|
||
|
|
- Tenant-Microsoft-SSO ist separat abgesichert:
|
||
|
|
- `tenants.sso_manage` für Pflege der Tenant-SSO-Konfiguration.
|
||
|
|
- Shared-App-Credentials bleiben unter `settings.update`.
|
||
|
|
- Secrets werden nur verschlüsselt gespeichert (`APP_CRYPTO_KEY` + AES-256-GCM), nie im Klartext gerendert.
|
||
|
|
- Profil-Sync ist tenant-spezifisch konfigurierbar (`sync_profile_on_login`, `sync_profile_fields`).
|
||
|
|
- Erlaubte Sync-Felder: `first_name`, `last_name`, `phone`, `mobile`, `avatar`.
|
||
|
|
- Graph-basierte Felder (`phone/mobile/avatar`) laufen fail-open: Login bleibt erfolgreich, auch wenn Graph nicht verfügbar ist.
|
||
|
|
|
||
|
|
## 5) Tenant Scope
|
||
|
|
|
||
|
|
- Datenzugriff für tenant-gebundene Inhalte erfolgt nur bei Tenant-Match.
|
||
|
|
- Departments sind genau einem Tenant zugeordnet (`departments.tenant_id`).
|
||
|
|
- Beim Speichern von User-Zuordnungen werden Departments serverseitig gegen die aktuell zugewiesenen User-Tenants gefiltert; ungültige Department-Zuordnungen werden entfernt.
|
||
|
|
- User-Zusatzfeldwerte werden auf Tenant-Wechsel bereinigt (Werte außerhalb der aktuell zugewiesenen Tenants werden gelöscht).
|
||
|
|
- Filterbare Zusatzfelder sind auf sichere Typen begrenzt (`select`, `multiselect`, `boolean`, `date`); freie Texttypen werden nicht als dynamische Filter zugelassen.
|
||
|
|
- Theme-Auflösung ist tenant-spezifisch: `default_theme` und `allow_user_theme` werden je `current_tenant` aufgelöst; ohne Tenant-Snapshot greifen globale Defaults.
|
||
|
|
- SSO-Mapping bleibt tenant-scoped:
|
||
|
|
- Primär über externe Identität (`provider + tid + oid`).
|
||
|
|
- Fallback einmalig über E-Mail (create-or-link), danach feste Identity-Linkung.
|
||
|
|
- Domain-Allowlist (falls gesetzt) wird im Callback serverseitig erzwungen.
|
||
|
|
- Profil-Sync überschreibt nur konfigurierte Felder und ignoriert leere Quelle-Werte;
|
||
|
|
`email` bleibt nach JIT-Create unverändert (insert-only).
|
||
|
|
- Lokaler/SSO-Login ist nur mit mindestens einem aktiven User-Tenant erlaubt (kein Sonderfall ohne Tenant).
|
||
|
|
- Strict/Non-Strict Verhalten über `TENANT_SCOPE_STRICT`.
|
||
|
|
- Inaktiv-Status von Tenants beeinflusst Sichtbarkeit (je nach Kontext).
|
||
|
|
|
||
|
|
## 6) Verbotene Zugriffe
|
||
|
|
|
||
|
|
- Bei fehlender Permission/Tenant-Bindung: forbidden flow (403 Seite) statt stilles Durchrutschen.
|
||
|
|
- Keine sensitiven Details in Fehlermeldungen leaken.
|
||
|
|
|
||
|
|
## 7) Datei-/Media-Endpunkte
|
||
|
|
|
||
|
|
- Avatar/Favicon/Logo Endpunkte prüfen Session + Zugriffskontext.
|
||
|
|
- Storage liegt außerhalb direkter Business-Logik; Zugriff nur über kontrollierte Endpunkte/Symlinks.
|
||
|
|
|
||
|
|
## 8) CSRF und Form-Schutz
|
||
|
|
|
||
|
|
- Schreibaktionen nutzen CSRF-Token.
|
||
|
|
- Delete/Status-Aktionen nur per POST und serverseitiger Validierung.
|
||
|
|
- Bulk-Download für Onboarding-PDF (`admin/users/access-pdf-bulk`) ist POST+CSRF, Scope bleibt aktiv.
|
||
|
|
- OIDC-Loginfluss nutzt `state`, `nonce` und PKCE; Callback validiert zusätzlich `aud`, `iss`, `tid`, Signatur (JWKS), `exp/nbf`.
|
||
|
|
|
||
|
|
## 9) Operative Checks
|
||
|
|
|
||
|
|
Regelmäßig prüfen:
|
||
|
|
|
||
|
|
- Inaktive Rollen/Permissions werden nicht mehr aktiv zugewiesen/ausgewertet.
|
||
|
|
- Tenant-Entzüge invalidieren Sichtkontext erwartungsgemäß.
|
||
|
|
- Remember-Tokens können zentral invalidiert werden.
|
||
|
|
- Mail-Log enthält Fehlerdaten für Auditing.
|
||
|
|
- User-Lifecycle-Policy (falls aktiv) läuft täglich und setzt inaktive Accounts automatisch auf `inactive`
|
||
|
|
bzw. löscht sie später (2-stufig), privilegierte Admins sind ausgenommen.
|
||
|
|
- Delete schreibt vorher zwingend ein Audit-Snapshot (fail-closed).
|
||
|
|
- Snapshot ist verschlüsselt gespeichert (`snapshot_enc`) und enthält keine Secrets/Tokens.
|
||
|
|
- Restore ist separat berechtigt (`users.lifecycle_restore`) und stellt bewusst keine Assignments wieder her.
|
||
|
|
- Geplante Aufgaben (Scheduler) sind Whitelist-basiert:
|
||
|
|
- keine frei ausführbaren Shell-Kommandos aus der Datenbank.
|
||
|
|
- zentrale Rechte: `jobs.view`, `jobs.manage`, `jobs.run_now`.
|
||
|
|
- Runner-Lock verhindert parallele Mehrfach-Ausführung.
|
||
|
|
|
||
|
|
## 10) API-Audit
|
||
|
|
|
||
|
|
- Alle `/api/v1/*` Requests werden zentral mit Metadaten in `api_audit_log` protokolliert.
|
||
|
|
- V1 speichert keine Request-/Response-Bodies, nur Metadaten (Pfad, Status, Dauer, Scope-Kontext).
|
||
|
|
- Query-Parameter werden redacted gespeichert (z. B. Token/Secret/Password-Felder).
|
||
|
|
- Zugriff auf die Audit-UI ist über Permission `api_audit.view` abgesichert.
|
||
|
|
- Retention ist auf 90 Tage ausgelegt (Bereinigung per Admin-Action oder Cron).
|
||
|
|
|
||
|
|
## 11) User-Lifecycle-Audit
|
||
|
|
|
||
|
|
- Lifecycle-Audit-UI ist über `user_lifecycle_audit.view` abgesichert.
|
||
|
|
- Restore aus Delete-Snapshot ist getrennt über `users.lifecycle_restore` abgesichert.
|
||
|
|
- Ohne gültigen Snapshot wird kein Auto-Delete ausgeführt (fail-closed).
|
||
|
|
- Retention für Lifecycle-Audit-Einträge: 365 Tage.
|