8. Authentifizierung und Rechte
Authentifizierung und Rechte
Abschnitt betitelt „Authentifizierung und Rechte“Die Anmeldung ist die Stelle, an der selbstgebaute Anwendungen am zuverlässigsten brechen. Nicht, weil sie schwer zu programmieren wäre, sondern weil sie an zwanzig kleinen Stellen richtig sein muss und an einer einzigen falschen Stelle wertlos wird. Ein perfekt gehashtes Passwort nützt nichts, wenn die Sitzung nie abläuft. Eine saubere Rollenprüfung nützt nichts, wenn sie nur im Frontend stattfindet.
Dieses Kapitel baut die Anmeldung einmal vollständig, im getrennten Aufbau aus Kapitel 5 und mit der Datenbank aus Kapitel 6.
Zwei Fragen, oft verwechselt
Abschnitt betitelt „Zwei Fragen, oft verwechselt“| Authentifizierung | Autorisierung | |
|---|---|---|
| Frage | Wer sind Sie? | Was dürfen Sie? |
| Antwort bei Misserfolg | 401 | 403 |
| Wie oft | einmal pro Sitzung | bei jeder Anfrage |
| Ergebnis | eine Identität | eine Entscheidung über eine konkrete Handlung |
Die dritte Zeile ist die, die im Unterricht am häufigsten untergeht. Die Anmeldung passiert einmal, die Berechtigungsprüfung bei jedem einzelnen Aufruf, für jedes einzelne Objekt. Wer das umdreht und einmalig prüft, ob jemand „Verwalter ist”, und danach alles erlaubt, hat eine Anwendung mit einem einzigen Schloss an der Eingangstür und offenen Türen dahinter.
Passwörter
Abschnitt betitelt „Passwörter“Was nicht geht
Abschnitt betitelt „Was nicht geht“Passwörter werden nicht im Klartext gespeichert. Sie werden auch nicht „verschlüsselt”, und dieser Fehler ist verbreiteter als der erste: Verschlüsselung ist umkehrbar, das ist ihr Zweck. Wer den Schlüssel hat, hat alle Passwörter, und der Schlüssel liegt auf demselben Server.
Und sie werden nicht mit MD5 oder SHA-1 gehasht, auch nicht mit SHA-256. Diese Verfahren sind schnell, und Schnelligkeit ist bei Passwörtern der Fehler: Eine Grafikkarte prüft Milliarden Kandidaten pro Sekunde. Ein Passwort-Hashverfahren ist absichtlich langsam und absichtlich speicherhungrig.
Was geht
Abschnitt betitelt „Was geht“Argon2id ist die aktuelle Empfehlung, bcrypt die etablierte Alternative. Beide erzeugen den Salt selbst, hängen ihn an den Hash und speichern die Parameter mit, sodass sich später alles aus der gespeicherten Zeichenkette rekonstruieren lässt.
import argon2 from "argon2";
// during registrationconst hash = await argon2.hash(password, { type: argon2.argon2id, memoryCost: 19456, // 19 MiB timeCost: 2, parallelism: 1,});
// during sign-inconst matches = await argon2.verify(hash, enteredPassword);Die Parameter stammen aus den OWASP-Empfehlungen und sind ein Kompromiss zwischen Sicherheit und Serverlast. Wählen Sie sie so groß, dass eine Prüfung auf Ihrem Server ungefähr eine Zehntelsekunde dauert.
Regeln, die wirken
Abschnitt betitelt „Regeln, die wirken“Die Passwortregeln, die viele Anwendungen erzwingen, stammen aus den Neunzigerjahren und richten mehr Schaden als Nutzen an. Was tatsächlich hilft:
- Länge statt Zeichenklassen. Mindestens zwölf Zeichen, und alles erlauben, was jemand tippen kann. Ein erzwungenes Sonderzeichen führt zu
Password1!. - Abgleich mit bekannten Leaks. Dienste wie Have I Been Pwned lassen sich datensparsam abfragen, ohne das Passwort zu übertragen. Ein Passwort, das in einem Leak steht, ist unabhängig von seiner Komplexität verbrannt.
- Keine erzwungene Änderung alle 90 Tage. Das erzeugt
Sommer2026!, gefolgt vonHerbst2026!. - Obergrenze nicht zu niedrig. Passphrasen sind gut, und 64 Zeichen sollten durchgehen.
Zurücksetzen
Abschnitt betitelt „Zurücksetzen“Der Ablauf zum Zurücksetzen ist eine eigene Angriffsfläche und braucht vier Eigenschaften: Der Link enthält ein zufälliges Token mit genügend Entropie, dieses Token wird in der Datenbank gehasht abgelegt (es ist ein Passwort auf Zeit), es gilt kurz, üblicherweise 15 bis 60 Minuten, und es lässt sich genau einmal verwenden. Nach dem Zurücksetzen werden alle bestehenden Sitzungen beendet.
Sitzungen
Abschnitt betitelt „Sitzungen“Bei einer serverseitigen Sitzung bekommt der Browser nur eine zufällige Sitzungs-ID in einem Cookie. Alles Weitere liegt auf dem Server, in der Datenbank oder in einem schnellen Speicher.
const cookieStore = await cookies();
cookieStore.set("session", sessionId, { httpOnly: true, // no access from JavaScript secure: true, // only over HTTPS sameSite: "lax", // not sent along on cross-site requests path: "/", maxAge: 60 * 60 * 8,});Drei Punkte gehören zum Betrieb einer Sitzung dazu.
Zwei Ablaufzeiten. Eine absolute Lebensdauer, nach der neu angemeldet werden muss, und ein Inaktivitätsablauf, der greift, wenn jemand die Anwendung offen liegen lässt. Beide gehören konfiguriert; eine Sitzung ohne Ablauf ist eine dauerhafte Vollmacht.
Neue Sitzungs-ID nach der Anmeldung. Wer sich anmeldet, bekommt eine frische ID. Sonst kann ein Angreifer dem Opfer vorher eine bekannte ID unterschieben und ist nach dessen Anmeldung mit angemeldet. Dieser Angriff heißt Session Fixation und ist mit einer Zeile Code erledigt.
Abmelden, das wirkt. Die Sitzung wird serverseitig gelöscht, und zwar auf Wunsch auch auf allen Geräten. Das ist der größte praktische Vorteil dieses Verfahrens gegenüber Tokens.
Ein JWT besteht aus drei mit Punkten getrennten Teilen: Kopf, Nutzdaten und Signatur, jeweils Base64url-kodiert.
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI0MiIsInJvbGxlIjoiYWRtaW4ifQ.dBjftJ... Kopf Nutzdaten SignaturDer übliche Aufbau kombiniert ein kurzlebiges Zugriffs-Token (5 bis 15 Minuten) mit einem längerlebigen Refresh-Token, mit dem sich ein neues Zugriffs-Token holen lässt. Das Refresh-Token wird bei jeder Verwendung ausgetauscht (Rotation), und wenn ein bereits benutztes Refresh-Token noch einmal auftaucht, ist das ein Hinweis auf Diebstahl; dann wird die ganze Kette ungültig gemacht.
Das eigentliche Problem bei Tokens ist der Rückruf. Ein signiertes Token ist gültig, bis es abläuft, auch wenn Sie den Benutzer inzwischen gesperrt haben, denn zur Prüfung wird nichts nachgeschlagen. Es gibt drei Antworten darauf: sehr kurze Laufzeiten, eine Sperrliste, oder eine Sitzungs-ID im Token, die serverseitig geprüft wird. Die dritte Variante funktioniert gut und führt gedanklich zurück zu den Sitzungen.
| Serverseitige Sitzung | JWT | |
|---|---|---|
| Prüfung | Nachschlagen im Speicher | Signatur prüfen, ohne Nachschlagen |
| Abmelden | sofort wirksam | erst mit Ablauf oder über Zusatzaufwand |
| Skalierung | gemeinsamer Sitzungsspeicher nötig | zustandslos, gut verteilbar |
| Fremde Clients | umständlich | natürlich |
| Typisches Risiko | Sitzungsdiebstahl, CSRF | Diebstahl aus dem Speicher, kein Rückruf |
| Aufwand | gering | mittel bis hoch, wenn man es richtig macht |
Für ein Jahresprojekt mit einem Browser-Frontend ist die serverseitige Sitzung mit HttpOnly-Cookie die vernünftigere Wahl: weniger Code, sofortiges Abmelden, kein Token in JavaScript. Tokens werden interessant, sobald eine Mobil-App oder ein fremdes System dazukommt.
Autorisierung
Abschnitt betitelt „Autorisierung“Rollen und Regeln
Abschnitt betitelt „Rollen und Regeln“RBAC ordnet Benutzern Rollen zu und Rollen Rechte. Das reicht für die meisten Projekte. ABAC entscheidet anhand von Eigenschaften, etwa „darf bearbeiten, solange die Buchung in der Zukunft liegt und ihm selbst gehört”. In der Praxis mischt man beides.
Der erste Schritt ist eine Matrix, und die gehört ins Repository:
| Aktion | Gast | Mitglied | Werkstattleitung |
|---|---|---|---|
| Maschinenliste sehen | ja | ja | ja |
| Belegung sehen | nein | ja | ja |
| Buchung anlegen | nein | ja, mit Einschulung | ja |
| Fremde Buchung ändern | nein | nein | ja |
| Eigene Buchung stornieren | nein | ja, bis 24 h vorher | ja |
| Einschulung eintragen | nein | nein | ja |
Diese Tabelle ist gleichzeitig die Testliste. Für jede Zeile mit „nein” gibt es einen Test, der nachweist, dass es tatsächlich nicht geht.
An einer Stelle prüfen
Abschnitt betitelt „An einer Stelle prüfen“Die Prüfung gehört in die Dienstschicht aus Kapitel 6, nicht in die Route Handler und erst recht nicht in die Komponenten. Der Grund ist einfach: In den Handler schreibt man sie beim vierten Endpunkt nicht mehr hinein, und in der Komponente ist sie kein Schutz.
export async function updateBooking(id: number, data: BookingUpdate, user: User) { const booking = await repo.findBooking(id); if (!booking) throw new NotFound();
if (booking.memberId !== user.id && !user.roles.includes("manager")) { throw new NotFound(); // deliberately not Forbidden } // ...}Die häufigste Lücke
Abschnitt betitelt „Die häufigste Lücke“Eine Rollenprüfung allein genügt nicht. Die verbreitetste Schwachstelle in Webanwendungen ist, dass ein angemeldeter Benutzer mit der richtigen Rolle einfach eine fremde ID in die URL schreibt und fremde Daten bekommt. Der Name dafür ist IDOR, Insecure Direct Object Reference, und die Prüfung dagegen lautet nicht „darf dieser Benutzer Buchungen ändern”, sondern „darf dieser Benutzer diese Buchung ändern”.
Im Code oben steht deshalb NotFound statt NichtErlaubt. Ein 403 auf eine fremde ID bestätigt, dass es diese Buchung gibt; ein 404 verrät nichts. Diese Entscheidung ist eine Abwägung, weil 404 die Fehlersuche erschwert, und sie gehört ins Entscheidungsprotokoll.
Probieren Sie das an Ihrem eigenen Projekt aus, bevor Sie es absichern: Legen Sie zwei Konten an, buchen Sie mit dem einen, und rufen Sie mit dem anderen dieselbe ID auf.
Frontend-Schutz ist kein Schutz
Abschnitt betitelt „Frontend-Schutz ist kein Schutz“Eine ausgeblendete Schaltfläche, eine Weiterleitung auf die Anmeldeseite, eine Route mit Rollenprüfung im Frontend: All das ist Bedienkomfort. Der Beweis dauert eine Zeile:
curl -i https://api.projekt.example.at/bookings/812 \ -X PATCH -H "Content-Type: application/json" -d '{"end":"2026-03-12T18:00:00Z"}'Wenn hier etwas anderes als 401 oder 403 kommt, ist der Endpunkt offen, unabhängig davon, wie gut die Oberfläche aussieht. Genau dieser Aufruf gehört als Nachweis in die Abgabe.
Anmeldung im getrennten Aufbau
Abschnitt betitelt „Anmeldung im getrennten Aufbau“Wo die Anmeldung lebt, hängt an der Entscheidung aus Kapitel 5.
Im BFF-Modell hält der Frontend-Server die Sitzung, der Browser hat ein HttpOnly-Cookie, und das Token für die API bleibt serverseitig. Damit ist das Diebstahlrisiko aus JavaScript ausgeschlossen, und es bleibt die CSRF-Frage, die in Kapitel 9 behandelt wird.
Im Bearer-Modell hält der Client das Token und schickt es im Authorization-Header. CSRF entfällt weitgehend, weil nichts automatisch mitgeschickt wird, dafür ist jede XSS-Lücke ein Sitzungsverlust.
Für nachgelagerte Dienste gilt eine eigene Regel: Wenn Ihre Anwendung mit dem CMS oder einem fremden Dienst spricht, bekommt sie dafür eine eigene Identität, also ein Dienstkonto mit eigenen Rechten. Das Benutzertoken einfach weiterzureichen gibt dem nachgelagerten Dienst genau die Rechte des Benutzers, und meist auch die, die er nicht braucht.
Fremde Identitätsanbieter
Abschnitt betitelt „Fremde Identitätsanbieter“Statt selbst Passwörter zu verwalten, kann man die Anmeldung an einen Anbieter delegieren. OAuth 2.0 regelt dabei die Erlaubnisvergabe, OpenID Connect setzt die Identität obendrauf.
Vier Beteiligte sind im Spiel: der Benutzer, Ihre Anwendung als Client, der Autorisierungsserver beim Anbieter und die geschützte Ressource. Der heute empfohlene Ablauf ist der Authorization Code Flow mit PKCE:
sequenceDiagram participant B as Browser participant A as Ihre Anwendung participant I as Identitätsanbieter B->>A: Anmelden mit fremdem Konto A->>B: Weiterleitung zum Anbieter, mit code_challenge B->>I: Anmeldung und Zustimmung I->>B: Weiterleitung zurück, mit Autorisierungscode B->>A: Autorisierungscode A->>I: Code plus code_verifier I->>A: ID-Token und Zugriffstoken A->>B: eigene Sitzung setzen
Der Punkt an diesem Ablauf: Das eigentliche Token geht nie durch den Browser, sondern wird von Server zu Server geholt. Der code_verifier stellt sicher, dass ein abgefangener Autorisierungscode allein nichts nützt.
Für Ihr Projekt ist das eine optionale Erweiterung („Anmelden mit GitHub”), keine Pflicht. Die Vertiefung folgt in der 5. Klasse, dort dann mit Datenschutzfragen und der Abwägung, ob man einem fremden Anbieter die Anmeldung überlassen will. Ebenfalls als Ausblick: die Zwei-Faktor-Anmeldung über TOTP, bei der zusätzlich ein zeitbasierter Code aus einer Authenticator-App verlangt wird.
Häufige Stolperfallen
Abschnitt betitelt „Häufige Stolperfallen“- Krypto oder Sitzungsverwaltung selbst bauen, weil es nur ein Schulprojekt ist. Der Fehler fällt nicht auf und macht alles wertlos.
- Passwörter mit SHA-256 hashen. Schnell ist hier die falsche Eigenschaft.
- JWT im
localStorageplus eine XSS-Lücke. Die Sitzung ist damit kopierbar und nicht rückrufbar. - Unterschiedliche Meldungen oder Antwortzeiten für unbekannte Kennung und falsches Passwort.
- Rollenprüfung nur im Frontend. Die API bleibt offen, und
curlweiß das. - Keine objektbezogene Prüfung. Eine fremde ID in der URL liefert fremde Daten, und niemand merkt es.
- Sitzungs-ID nach der Anmeldung beibehalten. Session Fixation, mit einer Zeile vermeidbar.
- Kein Ablauf, kein Abmelden. Eine Sitzung ohne Ende ist eine Vollmacht ohne Ende.
- Benutzertoken an nachgelagerte Dienste weiterreichen, statt dem Dienst eine eigene Identität zu geben.
Lernergebnisse
Abschnitt betitelt „Lernergebnisse“- Erklären: Authentifizierung und Autorisierung unterscheiden und begründen, warum die zweite bei jeder Anfrage stattfindet.
- Erklären: begründen, warum Passwörter gehasht und nicht verschlüsselt werden und warum ein schnelles Hashverfahren ungeeignet ist.
- Anwenden: Passwörter mit Argon2id oder bcrypt korrekt speichern und prüfen.
- Anwenden: Registrierung, Anmeldung, Abmeldung und Zurücksetzen mit Einmal-Token umsetzen.
- Erklären: erklären, warum ein JWT lesbar ist, und daraus ableiten, was nicht hineingehört.
- Analysieren: Sitzungs- und Token-Verfahren hinsichtlich Abmeldung, Skalierung, XSS und CSRF vergleichen und eine begründete Wahl treffen.
- Erschaffen: eine Rollenmatrix für das eigene Projekt aufstellen und in Tests überführen.
- Anwenden: Rollen- und Objektrechte zentral in der Dienstschicht durchsetzen.
- Analysieren: eine IDOR-Lücke im eigenen Projekt nachweisen und schließen.
- Beurteilen: nachweisen, dass ein geschützter Endpunkt auch ohne Frontend nicht erreichbar ist.
- Erklären: den Authorization Code Flow mit PKCE in Grundzügen erklären.
Arbeit am Jahresprojekt
Abschnitt betitelt „Arbeit am Jahresprojekt“- Registrierung, Anmeldung, Abmeldung und Passwort-Zurücksetzen funktionieren, Passwörter sind mit Argon2id oder bcrypt gespeichert.
- Sitzungen haben Ablauf und lassen sich beenden; nach der Anmeldung wird die Sitzungs-ID gewechselt.
- Die Rollenmatrix liegt in
docs/architektur.md, mindestens zwei Rollen sind umgesetzt. - Objektbezogene Rechte sind implementiert, ein IDOR-Versuch ist als Protokoll dokumentiert (vorher erfolgreich, nachher nicht).
- Für jede „nein”-Zelle der Matrix existiert ein Test.
- Ein
curl-Aufruf gegen einen geschützten Endpunkt ohne Anmeldung liegt als Nachweis bei.
Passende Übungen
Abschnitt betitelt „Passende Übungen“- Aufgabe - Anmeldung von Grund auf (Hashing, Sitzung, Abmeldung)
- Aufgabe - Sitzung gegen JWT: dieselbe Anwendung in zwei Varianten
- Aufgabe - IDOR: fremde Daten über die ID abrufen und die Lücke schließen
- Aufgabe - Rollenmatrix für das eigene Projekt umsetzen und testen