initial
This commit is contained in:
117
docs/security.md
Normal file
117
docs/security.md
Normal file
@@ -0,0 +1,117 @@
|
||||
# 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.
|
||||
|
||||
## 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.
|
||||
|
||||
## 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.
|
||||
Reference in New Issue
Block a user