score

Mandanten und Sicherheit

Wie score Schulen voneinander trennt, Personen anmeldet und Noten schützt.

Mandanten

Eine Einrichtung – meist eine Schule – ist ein Mandant. Darüber liegen Verwaltungsebenen wie Träger oder Land. Jede Fachtabelle trägt die Einrichtung, zu der eine Zeile gehört.

Eine Mitgliedschaft verbindet Person, Einrichtung, Rolle und Zeitraum. Eine Lehrkraft an zwei Schulen hat zwei Mitgliedschaften und sieht beide Schulen, aber jede nur in ihrer Rolle.

Row-Level Security

Die Trennung liegt in PostgreSQL, nicht nur in der Anwendung:

  • score-api setzt zu Beginn jeder Transaktion Person und erlaubte Einrichtungen. Der Kontext gilt nur für diese Transaktion und kommt aus den heute gültigen Mitgliedschaften, nie aus der Anfrage.
  • Die Anwendung arbeitet unter einer eigenen Datenbankrolle ohne BYPASSRLS und ohne Eigentümerrechte. Migrationen laufen getrennt davon.
  • Policies sind mit FORCE ROW LEVEL SECURITY aktiv. Ohne Kontext liefert keine Fachtabelle eine Zeile.
  • Schreibrechte prüft ebenfalls die Datenbank: Noten schreibt nur, wer den Kurs unterrichtet; Entsperren darf nur die Schulleitung. Verstöße werden zu HTTP 403.

Tests legen je Fall eine eigene Datenbank an und prüfen als Anwendungsrolle unter anderem: keine Sonderrechte, ohne Kontext keine Zeilen, Lehrkraft an zwei Schulen, Schreiben in eine fremde Einrichtung scheitert, eine Schülerin sieht nur ihre eigenen Daten.

Anmeldung

Fokus
Anmeldung: OIDC in score-api, Sitzung als Cookie
Anmeldung: OIDC in score-api, Sitzung als CookieSequenzdiagramm mit 5 Beteiligten: Browser (Akteur), score-web, score-api, Keycloak und PostgreSQL (Datenbank). 15 Nachrichten, in Reihenfolge: 1. Browser an score-web: GET /auth/login. 2. score-web an score-api: durchgereicht. 3. score-api an Browser: Weiterleitung, PKCE, Nonce (Antwort). 4. Browser an Keycloak: Anmeldung beim IdP. 5. Keycloak an Browser: Code (Antwort). 6. Browser an score-api: /auth/callback über score-web. 7. score-api an Keycloak: Code tauschen. 8. Keycloak an score-api: ID-Token (Antwort). 9. score-api an PostgreSQL: Konto, Person, Sitzung (Hash). 10. score-api an Browser: Cookie score_session (Antwort). 11. Browser an score-web: GET /kurse. 12. score-web an score-api: GET /v1/kurse mit Cookie. 13. score-api an PostgreSQL: Kontext setzen, SELECT. 14. PostgreSQL an score-api: nur eigene Einrichtungen (Antwort). 15. Keycloak an score-api: Back-Channel-Logout (asynchron). Abschnitt opt „zentrale Abmeldung“ umfasst Nachricht 15.opt[zentrale Abmeldung]1. Browser → score-web: GET /auth/loginGET /auth/login12. score-web → score-api: durchgereichtdurchgereicht23. score-api → Browser: Weiterleitung, PKCE, NonceWeiterleitung, PKCE, Nonce34. Browser → Keycloak: Anmeldung beim IdPAnmeldung beim IdP45. Keycloak → Browser: CodeCode56. Browser → score-api: /auth/callback über score-web/auth/callback über score-web67. score-api → Keycloak: Code tauschenCode tauschen78. Keycloak → score-api: ID-TokenID-Token89. score-api → PostgreSQL: Konto, Person, Sitzung (Hash)Konto, Person, Sitzung (Hash)910. score-api → Browser: Cookie score_sessionCookie score_session1011. Browser → score-web: GET /kurseGET /kurse1112. score-web → score-api: GET /v1/kurse mit CookieGET /v1/kurse mit Cookie1213. score-api → PostgreSQL: Kontext setzen, SELECTKontext setzen, SELECT1314. PostgreSQL → score-api: nur eigene Einrichtungennur eigene Einrichtungen1415. Keycloak → score-api: Back-Channel-LogoutBack-Channel-Logout15BrowserBrowserscore-web – Astro SSRscore-webAstro SSRscore-api – Goscore-apiGoKeycloak – BrokerKeycloakBrokerPostgreSQL – RLSPostgreSQLRLS
Anmeldung: OIDC in score-api, Sitzung als CookieSequenzdiagramm mit 5 Beteiligten: Browser (Akteur), score-web, score-api, Keycloak und PostgreSQL (Datenbank). 15 Nachrichten, in Reihenfolge: 1. Browser an score-web: GET /auth/login. 2. score-web an score-api: durchgereicht. 3. score-api an Browser: Weiterleitung, PKCE, Nonce (Antwort). 4. Browser an Keycloak: Anmeldung beim IdP. 5. Keycloak an Browser: Code (Antwort). 6. Browser an score-api: /auth/callback über score-web. 7. score-api an Keycloak: Code tauschen. 8. Keycloak an score-api: ID-Token (Antwort). 9. score-api an PostgreSQL: Konto, Person, Sitzung (Hash). 10. score-api an Browser: Cookie score_session (Antwort). 11. Browser an score-web: GET /kurse. 12. score-web an score-api: GET /v1/kurse mit Cookie. 13. score-api an PostgreSQL: Kontext setzen, SELECT. 14. PostgreSQL an score-api: nur eigene Einrichtungen (Antwort). 15. Keycloak an score-api: Back-Channel-Logout (asynchron). Abschnitt opt „zentrale Abmeldung“ umfasst Nachricht 15.opt[zentrale Abmeldung]1. Browser → score-web: GET /auth/loginGET /auth/login12. score-web → score-api: durchgereichtdurchgereicht23. score-api → Browser: Weiterleitung, PKCE, NonceWeiterleitung, PKCE, Nonce34. Browser → Keycloak: Anmeldung beim IdPAnmeldung beim IdP45. Keycloak → Browser: CodeCode56. Browser → score-api: /auth/callback über score-web/auth/callback überscore-web67. score-api → Keycloak: Code tauschenCode tauschen78. Keycloak → score-api: ID-TokenID-Token89. score-api → PostgreSQL: Konto, Person, Sitzung (Hash)Konto, Person, Sitzung(Hash)910. score-api → Browser: Cookie score_sessionCookie score_session1011. Browser → score-web: GET /kurseGET /kurse1112. score-web → score-api: GET /v1/kurse mit CookieGET /v1/kurse mitCookie1213. score-api → PostgreSQL: Kontext setzen, SELECTKontext setzen, SELECT1314. PostgreSQL → score-api: nur eigene Einrichtungennur eigene Einrichtungen1415. Keycloak → score-api: Back-Channel-LogoutBack-Channel-Logout15BrowserBrowserscore-web – Astro SSRscore-webAstro SSRscore-api – Goscore-apiGoKeycloak – BrokerKeycloakBrokerPostgreSQL – RLSPostgreSQLRLS

Quelle: score, api/internal/auth

Diagrammdaten anzeigen
Daten zu Anmeldung: OIDC in score-api, Sitzung als Cookie
Nr.VonAnNachrichtArtAbschnitt
1Browserscore-webGET /auth/loginAufruf
2score-webscore-apidurchgereichtAufruf
3score-apiBrowserWeiterleitung, PKCE, NonceAntwort
4BrowserKeycloakAnmeldung beim IdPAufruf
5KeycloakBrowserCodeAntwort
6Browserscore-api/auth/callback über score-webAufruf
7score-apiKeycloakCode tauschenAufruf
8Keycloakscore-apiID-TokenAntwort
9score-apiPostgreSQLKonto, Person, Sitzung (Hash)Aufruf
10score-apiBrowserCookie score_sessionAntwort
11Browserscore-webGET /kurseAufruf
12score-webscore-apiGET /v1/kurse mit CookieAufruf
13score-apiPostgreSQLKontext setzen, SELECTAufruf
14PostgreSQLscore-apinur eigene EinrichtungenAntwort
15Keycloakscore-apiBack-Channel-Logoutasynchronopt [zentrale Abmeldung]
  • OpenID Connect mit Authorization Code, PKCE und Nonce, ausgeführt in score-api. Der Browser erhält nur ein httpOnly-Cookie; in der Datenbank steht lediglich dessen SHA-256.
  • Keycloak brokert die Identitätsanbieter. Das Konto hängt am Subject des ursprünglichen Anbieters, weil sich die Keycloak-eigene Kennung bei einem Neuaufbau ändern kann.
  • Sitzungen enden nach Inaktivität (Standard 8 Stunden) und nach einer Höchstdauer (Standard 12 Stunden).
  • Back-Channel-Logout: Meldet Keycloak eine zentrale Abmeldung, beendet score alle Sitzungen dieser Anmeldung. Das Logout-Token wird auf Signatur, Zielgruppe, Ereignis und Alter geprüft.
  • Rücksprungziele nach der Anmeldung sind nur relative Pfade.

Browser-Schutz

  • Content-Security-Policy als HTTP-Header: Skripte und Styles nur mit Hashes, die beim Rendern jeder Seite entstehen, keine Ressourcen anderer Herkunft, kein Einbetten in fremde Seiten, Formulare nur an score selbst und an den Identitätsanbieter. Für die Notenvorschau ist WebAssembly erlaubt.
  • Dazu HSTS, X-Content-Type-Options, Referrer-Policy, Permissions-Policy und die Cross-Origin-Header. Seiten mit Noten dürfen nicht gecacht werden (Cache-Control: no-store).
  • Formulare prüfen die Herkunft der Anfrage; das Sitzungs-Cookie ist SameSite=Lax.
  • score-api liefert nur JSON, PDFs und Weiterleitungen und verbietet Browsern, davon etwas auszuführen oder einzubetten.

Schutzbedarf

Noten, Zeugnisse, Förderdaten und Identitäten haben den Schutzbedarf „hoch“. Daraus folgen der Betrieb nur in Deutschland, Schlüssel beim Kunden (im Landesprofil KMS oder HSM), die Trennung von fachlicher Administration und technischem Betrieb und ein Notfallzugang nur im Vier-Augen-Prinzip mit vollständigem Protokoll.