5.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:
- Browser ruft
/api/auth/loginauf. - Backend erzeugt
state,nonce,code_verifierund leitet zum IdP weiter. - IdP ruft
/api/auth/callbackmitcodeundstateauf. - Backend tauscht den Code gegen Tokens.
- Backend validiert ID Token, Issuer, Audience, Nonce und Algorithmus.
- Backend laedt optional UserInfo.
- Backend legt oder aktualisiert den lokalen Benutzer.
- 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:
@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:
AuthorizationCookieX-CSRF-TokenSet-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.