generated from bastian/boilerplate
136 lines
5.1 KiB
Markdown
136 lines
5.1 KiB
Markdown
# Security-Modell
|
|
|
|
Das Boilerplate trennt Browser, Backend und Identity Provider strikt. Der Browser
|
|
bekommt keine OIDC-Tokens. Das Backend ist fuer Authentifizierung,
|
|
Autorisierung, CSRF und Session-Verwaltung verbindlich.
|
|
|
|
## OIDC
|
|
|
|
Der Login nutzt Authorization Code Flow mit PKCE:
|
|
|
|
1. Browser ruft `/api/auth/login` auf.
|
|
2. Backend erzeugt `state`, `nonce`, `code_verifier` und leitet zum IdP weiter.
|
|
3. IdP ruft `/api/auth/callback` mit `code` und `state` auf.
|
|
4. Backend tauscht den Code gegen Tokens.
|
|
5. Backend validiert ID Token, Issuer, Audience, Nonce und Algorithmus.
|
|
6. Backend laedt optional UserInfo.
|
|
7. Backend legt oder aktualisiert den lokalen Benutzer.
|
|
8. Backend erzeugt eine serverseitige Session.
|
|
|
|
Erlaubte Signaturalgorithmen werden ueber `OIDC_ALLOWED_ALGORITHMS` gesetzt.
|
|
`none` ist explizit verboten.
|
|
|
|
Beim Logout wird zuerst die lokale Session widerrufen und das Session-/CSRF-Cookie
|
|
geloescht. Anschliessend redirectet das Backend zum OIDC
|
|
`end_session_endpoint` aus Discovery oder zur optionalen `OIDC_LOGOUT_URL`. Wenn
|
|
die Session ein ID-Token enthaelt, wird es nur als `id_token_hint` an den IdP
|
|
gegeben und nicht an das Frontend ausgeliefert.
|
|
|
|
## Sessions
|
|
|
|
Sessions liegen in MySQL. Das Session-Cookie enthaelt keine Tokens oder
|
|
Benutzerdaten, sondern nur eine signierte Session-ID.
|
|
|
|
Gespeichert werden unter anderem:
|
|
|
|
- verschluesselte OIDC-Tokens
|
|
- Hash des CSRF-Tokens
|
|
- Ablaufzeiten fuer Idle Timeout und absolutes Timeout
|
|
- User-Agent und IP fuer Anzeige und Audit-Kontext
|
|
- Revocation-Zeitpunkt
|
|
|
|
Die Token-Verschluesselung nutzt `SESSION_ENCRYPTION_KEY`. Cookie-Signaturen
|
|
nutzen `SESSION_SECRET`. Beide Werte muessen in Produktion echte Secrets sein.
|
|
|
|
## CSRF
|
|
|
|
Schreibende Methoden `POST`, `PUT`, `PATCH` und `DELETE` brauchen ein gueltiges
|
|
CSRF-Token. Das Frontend liest das CSRF-Cookie und sendet es als
|
|
`X-CSRF-Token`. Das Backend vergleicht nur den Hash gegen die Session.
|
|
|
|
Public Routes sind vom CSRF-Guard ausgenommen, sofern sie mit `@Public()`
|
|
markiert sind.
|
|
|
|
## Rollen und Permissions
|
|
|
|
Permissions werden in `apps/backend/src/roles/permissions.ts` definiert. Rollen
|
|
sind Datenbankdaten und referenzieren diese Permissions. Benutzer haben keine
|
|
direkten Permissions.
|
|
|
|
Backend-Controller schuetzen Endpunkte mit:
|
|
|
|
```ts
|
|
@RequirePermissions(Permission.ItemsRead)
|
|
```
|
|
|
|
Angular nutzt Permissions nur fuer Navigation und Darstellung. Eine versteckte
|
|
Schaltflaeche ist keine Sicherheitsgrenze.
|
|
|
|
Administrative Benutzer-, Rollen- und Session-Aktionen sind unter `/api/admin/*`
|
|
mit `users.read`, `users.manage`, `roles.read`, `roles.manage`,
|
|
`sessions.manage` und `audit.read` geschuetzt. Aktionen, die den letzten aktiven
|
|
Administrator entfernen koennten, laufen transaktional und antworten bei Verstoss
|
|
mit `LAST_ACTIVE_ADMIN_REQUIRED`.
|
|
|
|
Benachrichtigungen sind benutzerbezogene Daten. Normale Notification-Endpunkte
|
|
bestimmen den Benutzer ausschliesslich aus der serverseitig aufgeloesten Session.
|
|
Benutzer-IDs werden fuer eigene Benachrichtigungen nicht als Query-Parameter oder
|
|
Body-Feld akzeptiert. Fremde oder geloeschte Notification-IDs werden als nicht
|
|
gefunden behandelt.
|
|
|
|
## Benutzerstatus
|
|
|
|
Deaktivierte Benutzer werden trotz erfolgreichem IdP-Login abgewiesen. Aktive
|
|
Sessions deaktivierter Benutzer werden beim Session-Resolve unbrauchbar gemacht.
|
|
|
|
## Cookies und Proxy
|
|
|
|
In Produktion sollte TLS vor der Anwendung terminiert werden, zum Beispiel mit
|
|
Nginx. Setze `TRUST_PROXY=true`, wenn die Anwendung hinter einem vertrauenswuerdigen
|
|
Reverse Proxy laeuft und korrekte Forwarded Header benoetigt.
|
|
|
|
`APP_BASE_URL` muss die externe URL der Anwendung enthalten, weil daraus die OIDC
|
|
Callback URL gebaut wird.
|
|
|
|
## Logging und Redaction
|
|
|
|
Das Backend redigiert sensible Werte in Standard-HTTP-Logs:
|
|
|
|
- `Authorization`
|
|
- `Cookie`
|
|
- `X-CSRF-Token`
|
|
- `Set-Cookie`
|
|
- Access-, Refresh- und ID-Tokens
|
|
|
|
Neue Logs duerfen keine Secrets, Tokens, Session-IDs oder vollstaendige Cookies
|
|
enthalten. Fuer Auditing sollten fachliche IDs und Request IDs verwendet werden.
|
|
|
|
## Swagger
|
|
|
|
`SWAGGER_ENABLED` ist in Produktion standardmaessig `false`. Wenn Swagger in
|
|
Produktion aktiviert wird, sollte der Zugriff ueber Netzwerkregeln oder Reverse
|
|
Proxy zusaetzlich eingeschraenkt werden.
|
|
|
|
## Rate Limiting
|
|
|
|
Das konfigurierte Rate Limiting ist fuer eine einzelne Containerinstanz ohne
|
|
Redis ausgelegt. Die Zaehler liegen im Prozessspeicher. Bei horizontaler
|
|
Skalierung muss ein gemeinsamer Store eingefuehrt oder der Schutz an den Reverse
|
|
Proxy verlagert werden.
|
|
|
|
Es gibt zwei fruehe Ebenen:
|
|
|
|
- Globales IP-Limit fuer alle Requests, bevor Guards und Controller laufen.
|
|
- Strengeres IP-Limit fuer sensible Endpunkte, ebenfalls bevor Session-Resolve
|
|
und fachliche Datenbankzugriffe laufen.
|
|
|
|
Sensible Endpunkte werden mit `@SensitiveRateLimit()` markiert. Dazu gehoeren
|
|
Login, OIDC Callback, Logout, Session-Revoke sowie Benutzer- und
|
|
Rollenverwaltung. Bei Ueberschreitung antwortet die API mit HTTP 429,
|
|
`RATE_LIMIT_EXCEEDED`, `Retry-After` und `X-RateLimit-*` Headern.
|
|
|
|
Ein zusaetzliches Benutzer-Limit waere technisch moeglich, wuerde in diesem
|
|
Projekt aber erst nach dem serverseitigen Session-Resolve greifen. Fuer die
|
|
geforderte fruehe Lastreduktion ist deshalb das globale IP-Limit die verbindliche
|
|
Schutzschicht.
|