Files
boilerplate/docs/security.md
Bastian Wagner 543e8273a7 initial
2026-07-16 09:49:22 +02:00

4.1 KiB

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:

@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.