7. Anwendungssicherheit
Anwendungssicherheit
Abschnitt betitelt „Anwendungssicherheit“Im Vorjahr haben Sie bekannte Angriffe nachgestellt und geschlossen. Das war eine Prüfliste: Es gibt eine Liste bekannter Fehler, Sie gehen sie durch, Sie hakerln ab.
Prüflisten haben eine Grenze. Sie finden, was bei allen gleich ist, und übersehen, was an Ihrem System besonders ist. Kein Werkzeug der Welt bemerkt, dass in Ihrer Anwendung eine Werkstattaufsicht Buchungen fremder Personen stornieren darf, obwohl das niemand so wollte. Solche Fehler folgen aus dem Entwurf, und sie werden nur gefunden, wenn jemand vor dem Bauen darüber nachdenkt.
Diese zwei Wochen liefern dafür zwei Werkzeuge: ein Verfahren, um die Bedrohungen des eigenen Systems zu finden, und das Verständnis für die Mechanik, an der die meisten Anwendungen hängen, nämlich Identität.
Bedrohungsmodellierung
Abschnitt betitelt „Bedrohungsmodellierung“Vier Fragen
Abschnitt betitelt „Vier Fragen“Ein Bedrohungsmodell beantwortet vier Fragen, und man beantwortet sie am besten gemeinsam, an einer Tafel:
- Woran arbeiten wir? Ein Bild des Systems.
- Was kann schiefgehen? Die Bedrohungen.
- Was tun wir dagegen? Die Maßnahmen.
- Haben wir es gut gemacht? Die Prüfung.
Datenflüsse und Grenzen
Abschnitt betitelt „Datenflüsse und Grenzen“Zeichnen Sie Ihr System als Datenflussdiagramm: externe Beteiligte, Prozesse, Datenspeicher, Flüsse dazwischen. Und dann das Wichtigste, die Vertrauensgrenzen: die Linien, an denen Daten aus einem Bereich in einen anderen wechseln, dem Sie weniger oder mehr trauen.
flowchart LR
subgraph aussen[Nicht vertrauenswürdig]
B[Browser / App]
F[Fremder Dienst]
end
subgraph server[Ihr Server]
W[Webanwendung]
C[CMS]
D[(Datenbank)]
end
B -->|HTTPS| W
W --> D
C --> D
W -->|API-Aufruf| F
F -->|Webhook| W
Jede Linie, die eine Grenze kreuzt, ist eine Stelle, an der geprüft werden muss. Das gilt in beide Richtungen: Der Webhook des fremden Dienstes aus Kapitel 3 kommt von außen und wird behandelt wie jede andere fremde Eingabe.
Ein Raster, das für jeden Fluss und jeden Prozess sechs Fragen stellt. Sein Wert liegt darin, dass es Sie zu Bedrohungen zwingt, an die Sie nicht von selbst gedacht hätten.
| Buchstabe | Bedrohung | Beispiel im Projekt | Übliche Gegenmaßnahme |
|---|---|---|---|
| S | Identität vortäuschen | fremdes Sitzungscookie, geratenes Passwort | starke Anmeldung, MFA, sichere Cookies |
| T | Daten verändern | Preis im Formular manipuliert | serverseitige Prüfung, Integritätssicherung |
| R | Abstreiten | „Ich habe nie storniert” | Protokoll sicherheitsrelevanter Ereignisse |
| I | Informationen offenlegen | fremde Buchung über die ID abrufbar | Objektberechtigung, sparsame Fehlerausgabe |
| D | Dienst lahmlegen | teurer Export ohne Grenze, Anfrageflut | Ratenbegrenzung, Obergrenzen, Zeitlimits |
| E | Rechte ausweiten | Autor wird Administrator | Rollenprüfung serverseitig, keine Rechte im Client |
Gehen Sie die sechs Zeilen für die zwei bis drei wichtigsten Abläufe Ihres Projekts durch, nicht für alles. Ein Modell, das drei Tage kostet, wird nie aktualisiert.
Missbrauchsgeschichten
Abschnitt betitelt „Missbrauchsgeschichten“Das Gegenstück zur Nutzergeschichte, in derselben Form:
Als Angreifer möchte ich fremde Buchungs-IDs durchprobieren,um an Namen und Telefonnummern anderer Mitglieder zu kommen.
Gegenmaßnahme: Besitzprüfung im Service, Antwort 404 bei fremden Objekten.Nachweis: Regressionstest tests/autorisierung.test.ts, Zeile 14.Das lässt sich planen, schätzen und abhaken wie jede andere Aufgabe, und genau das ist der Zweck. Sicherheit, die nicht im Aufgabenbestand steht, wird nicht gemacht.
Bewerten und tragen
Abschnitt betitelt „Bewerten und tragen“Die Risikomatrix aus dem Vorjahr gilt unverändert: Auswirkung mal Wahrscheinlichkeit. Neu ist, dass Sie jetzt vor dem Bauen entscheiden, was Sie umsetzen, was später kommt und welches Restrisiko Sie bewusst tragen. Das Bedrohungsmodell ist kein Bild für die Wand: Es kommt nach docs/sicherheit.md und wird angesehen, sobald sich die Architektur ändert, also spätestens bei jedem neuen fremden Dienst.
Identität und Zugriff
Abschnitt betitelt „Identität und Zugriff“Wo die Sitzung wirklich lebt
Abschnitt betitelt „Wo die Sitzung wirklich lebt“Zur Erinnerung aus dem Vorjahr: Bei einer serverseitigen Sitzung hält das Cookie nur eine Kennung, die Wahrheit liegt in Ihrer Datenbank. Bei einem selbsttragenden Token steht der Inhalt im Token, und der Server glaubt der Signatur.
Der Unterschied wird beim Widerruf spürbar. Eine Sitzung löschen Sie, und sie ist weg. Ein gültig signiertes Token bleibt gültig, bis es abläuft, auch wenn Sie das Konto sperren. Deshalb sind Zugriffstoken kurzlebig, und deshalb ist für eine klassische Webanwendung die serverseitige Sitzung nach wie vor die einfachere und sicherere Wahl.
OAuth 2.0 ist Delegation
Abschnitt betitelt „OAuth 2.0 ist Delegation“Der Satz, an dem die meisten Missverständnisse hängen. OAuth beantwortet die Frage: „Darf diese Anwendung in meinem Namen auf diese Ressource zugreifen?” Es beantwortet nicht die Frage, wer ich bin. Dafür gibt es OpenID Connect, das auf OAuth aufsetzt.
Vier Rollen:
| Rolle | Wer das ist |
|---|---|
| Ressourcenbesitzer | die Person, deren Daten es sind |
| Client | Ihre Anwendung |
| Autorisierungsserver | der Identitätsanbieter, der Token ausstellt |
| Ressourcenserver | die Schnittstelle, die Token akzeptiert |
Der Ablauf mit PKCE
Abschnitt betitelt „Der Ablauf mit PKCE“sequenceDiagram participant B as Browser participant A as Ihre Anwendung participant I as Identitätsanbieter B->>A: Anmelden A->>A: code_verifier erzeugen, daraus code_challenge A->>B: Weiterleitung mit client_id, redirect_uri, state, code_challenge B->>I: Anmeldung und Zustimmung I->>B: Weiterleitung zurück mit code und state B->>A: Aufruf der Rückkehradresse A->>A: state prüfen A->>I: code und code_verifier einlösen I->>A: ID-Token und Access-Token A->>A: Token prüfen, eigene Sitzung anlegen A->>B: angemeldet
Jeder Parameter darin wehrt etwas Bestimmtes ab, und wer sie ohne dieses Wissen aus einem Blogbeitrag kopiert, lässt Lücken offen:
| Mechanismus | Wehrt ab |
|---|---|
state | Untergeschobene Anmeldung durch Dritte, also CSRF auf den Anmeldevorgang |
code_verifier und code_challenge (PKCE) | Abgefangener Autorisierungscode ist ohne den Verifier wertlos |
nonce | Wiedereinspielen eines fremden ID-Tokens |
| Genaue Übereinstimmung der Rückkehradresse | Umleitung des Codes an eine fremde Adresse |
| Austausch des Codes serverseitig | Das Client-Geheimnis verlässt den Server nicht |
Der frühere implizite Ablauf, bei dem das Token direkt in der Adresszeile zurückkam, ist genau deshalb abgeschafft: Adressen landen im Verlauf, in Protokollen und im Referer.
OpenID Connect darüber
Abschnitt betitelt „OpenID Connect darüber“OIDC ergänzt OAuth um das ID-Token, ein signiertes JWT mit Angaben über die angemeldete Person. Wichtig ist die Rollentrennung: Das Access-Token ist für die Schnittstelle bestimmt, das ID-Token für Ihre Anwendung. Beides zu verwechseln ist ein häufiger Fehler.
Ein ID-Token wird niemals ungeprüft geglaubt:
import { createRemoteJWKSet, jwtVerify } from "jose";
const keys = createRemoteJWKSet( new URL(`${process.env.OIDC_ISSUER}/.well-known/jwks.json`),);
export async function verifyIdToken(token: string, nonce: string) { const { payload } = await jwtVerify(token, keys, { issuer: process.env.OIDC_ISSUER, // iss audience: process.env.OIDC_CLIENT_ID, // aud: is it meant for us? algorithms: ["RS256"], // never accept "none" clockTolerance: 60, });
if (payload.nonce !== nonce) throw new Error("nonce does not match"); return payload;}Geprüft werden Signatur gegen den öffentlichen Schlüssel des Ausstellers, Aussteller, Zielgruppe, Ablauf und nonce. Die Adressen dafür stehen im Discovery-Dokument unter /.well-known/openid-configuration; die Schlüssel holt man von dort und speichert sie zwischen, statt sie fest einzutragen, weil sie gewechselt werden.
Welcher Ablauf wofür
Abschnitt betitelt „Welcher Ablauf wofür“| Fall | Empfehlung |
|---|---|
| Webanwendung mit Server | Autorisierungscode mit PKCE, Token bleiben serverseitig, eigene Sitzung im Cookie |
| Reines Frontend ohne Server | Backend for Frontend davorsetzen; Token gehören nicht in den Browser |
| Mobile App | Autorisierungscode mit PKCE im Systembrowser, nicht in einem eingebetteten Fenster |
| Dienst zu Dienst | Client Credentials, ohne Benutzer |
Lebensdauer und Abmeldung
Abschnitt betitelt „Lebensdauer und Abmeldung“Zugriffstoken leben kurz, typischerweise Minuten. Erneuerungstoken leben länger und werden bei jeder Verwendung ausgetauscht, damit ein gestohlenes altes Token auffällt. Und die Abmeldung ist mehr als das Löschen des eigenen Cookies: Wer sich zentral abmeldet, erwartet, dass die Sitzung überall endet. Wenn Ihr Anbieter das unterstützt, hängt daran ein Endpunkt, den er bei Ihnen aufruft.
Sicherer Entwurf
Abschnitt betitelt „Sicherer Entwurf“Validierung an jeder Vertrauensgrenze. Jede Eingabe, die eine Grenze aus Ihrem Diagramm kreuzt, wird geprüft, auch wenn sie schon einmal geprüft wurde. Der Browser, die App, der Webhook und das CMS sind alle außen.
Autorisierung an einer Stelle. Wenn die Berechtigungsprüfung in dreißig Routenhandlern steht, fehlt sie im einunddreißigsten. Sie gehört in die Dienstschicht, dorthin, wo die Fachlogik liegt.
Mandanten- und Besitztrennung im Datenmodell. Der Klassiker ist die vergessene Einschränkung in der Abfrage. Bauen Sie Ihre Zugriffe so, dass die Einschränkung nicht vergessen werden kann, etwa über eine Hilfsfunktion, die den Kontext des Benutzers immer mitnimmt.
Sichere Voreinstellungen. Neu angelegt heißt privat, gesperrt, ohne Rechte. Rechte werden vergeben, nicht entzogen.
Geringste Rechte in der ganzen Kette. Der Datenbankbenutzer der Anwendung darf keine Tabellen ändern, der Container läuft nicht als root, das Token für den fremden Dienst hat den kleinsten passenden Geltungsbereich.
Sicherheitsereignisse protokollieren. Anmeldung erfolgreich und fehlgeschlagen, Passwortwechsel, Rechteänderung, Administrationsaktion, Datenexport, Löschung. Ohne Passwörter, ohne Token, ohne mehr personenbezogene Daten als nötig, mit der Korrelations-ID aus dem Vorjahr. Das ist zugleich die Antwort auf das R in STRIDE und die Voraussetzung dafür, nach einem Vorfall überhaupt etwas rekonstruieren zu können.
Lieferkette
Abschnitt betitelt „Lieferkette“Ihr Projekt besteht zu 90 Prozent aus fremdem Code, den niemand im Team gelesen hat. Die Angriffe darauf sind real: übernommene Wartungskonten, Pakete mit Namen, die sich um einen Buchstaben unterscheiden, und Installationsskripte, die beim npm install ausgeführt werden.
npm ci --ignore-scripts # do not run install scriptsnpm audit --omit=dev # known vulnerabilities in the shipped statenpm sbom --sbom-format cyclonedx > sbom.jsonEine SBOM ist die Stückliste Ihrer Software: welches Paket in welcher Version steckt drin. Sie ist die Voraussetzung dafür, die Frage „sind wir von dieser Lücke betroffen?” in Minuten statt in Tagen zu beantworten.
Für Container kommt dasselbe eine Ebene tiefer: Das Basis-Image enthält ein Betriebssystem mit eigenen Lücken. Ein Prüfwerkzeug wie Trivy scannt das gebaute Image, und regelmäßiges Neubauen ist wichtiger als jede Einzelmaßnahme, weil ein Image von vor sechs Monaten sechs Monate alte Lücken enthält.
| Prüfung im Build | Fund führt zu |
|---|---|
| Geheimnisse im Repository | Abbruch, immer |
| Schwachstelle hoch oder kritisch in Produktionsabhängigkeiten | Abbruch |
| Schwachstelle mittel oder niedrig | Meldung, Eintrag in die Aufgabenliste |
| Veraltete Pakete | Meldung |
| Lizenz auf der Sperrliste | Abbruch |
Diese Tore in eine Pipeline einzubauen ist Thema von Kapitel 9; die Entscheidung, was blockiert und was nur meldet, treffen Sie hier. Eine Pipeline, die bei jedem mittleren Fund stehen bleibt, wird binnen zwei Wochen mit einem Schalter umgangen.
Secrets
Abschnitt betitelt „Secrets“Getrennt nach Umgebung seit Kapitel 1, im Passwortspeicher, nie im Repository und nie im Image. Dazu drei Punkte, die man erst braucht, wenn es zu spät ist:
Rotation. Jedes Geheimnis hat ein Datum. Wer im Team ausscheidet, löst eine Rotation aus, nicht nur eine Kontosperre.
Prüfung im Repository. Ein Werkzeug wie gitleaks läuft als Prüfung vor dem Commit und noch einmal in der Pipeline. Beides, weil das erste umgangen werden kann.
Vorgehen nach einem Leck, in dieser Reihenfolge:
-
Rotieren. Zuerst. Solange der Schlüssel gültig ist, ist alles andere Kosmetik.
-
Wirkung prüfen. Wurde er benutzt? Die Protokolle des Anbieters und Ihre eigenen sagen es.
-
Historie bereinigen. Erst jetzt, und im Wissen, dass Kopien existieren können.
-
Aufschreiben. Was ist passiert, wie ist es passiert, was verhindert die Wiederholung.
Die Reihenfolge ist der Lerninhalt. Wer mit dem Aufräumen der Git-Historie beginnt, verbringt eine Stunde damit, während der Schlüssel weiter gültig ist.
Werkzeuge finden die bekannten Klassen: statische Analyse im Code, Abhängigkeitsprüfung, und ein Scanner wie ZAP, der die laufende Anwendung abklopft. Letzteres läuft gegen Ihre Testumgebung, nicht gegen Produktion, und nicht gegen fremde Systeme.
Von Hand finden Sie das, was kein Werkzeug findet, nämlich Logikfehler: fremde Objekte über die ID, ein Ablauf, der sich in der Mitte abkürzen lässt, ein Preis, der vom Client kommt, eine Rolle, die mehr darf als gedacht. Genau dafür haben Sie das Bedrohungsmodell geschrieben.
Die gegenseitige Prüfung zwischen zwei Teams läuft wie im Vorjahr, mit schriftlicher Vereinbarung über Umfang und Grenzen, und mit Befunden, die dem System gelten.
Häufige Stolperfallen
Abschnitt betitelt „Häufige Stolperfallen“- Sicherheit als Kapitel am Ende statt als Entwurfsvorgabe.
- Bedrohungsmodell einmal gezeichnet und nie wieder angesehen.
- OAuth-Ablauf kopiert, ohne
stateund PKCE zu verstehen. - ID-Token ungeprüft geglaubt, ohne Aussteller, Zielgruppe und Signatur.
- Access-Token und ID-Token verwechselt.
- Token im Browser abgelegt, obwohl ein Server dazwischensteht.
- Autorisierung im Handler verstreut statt in der Dienstschicht.
- Einschränkung in der Abfrage vergessen, und schon sieht ein Mandant den anderen.
- Abhängigkeiten nie aktualisiert, weil es läuft.
- Installationsskripte blind ausgeführt.
- Geheimnisse zwischen Test und Produktion geteilt.
- Nach einem Leck zuerst die Historie aufgeräumt statt zuerst rotiert.
- Scanner gegen Produktion oder gegen fremde Systeme laufen lassen.
Lernergebnisse
Abschnitt betitelt „Lernergebnisse“- Analysieren: ein Datenflussdiagramm mit Vertrauensgrenzen für das eigene System zeichnen.
- Analysieren: ein Bedrohungsmodell nach STRIDE erstellen und Risiken priorisieren.
- Anwenden: Missbrauchsgeschichten formulieren und mit Gegenmaßnahme und Nachweis versehen.
- Erklären: OAuth 2.0 von OpenID Connect unterscheiden und die Rollen benennen.
- Erklären: den Autorisierungscode-Ablauf mit PKCE erklären und angeben, was jeder Mechanismus abwehrt.
- Anwenden: eine Anmeldung über einen Identitätsanbieter umsetzen und das ID-Token vollständig prüfen.
- Beurteilen: für einen gegebenen Anwendungsfall den passenden Ablauf wählen.
- Anwenden: sichere Voreinstellungen, zentrale Autorisierung und Mandantentrennung im Entwurf umsetzen.
- Anwenden: Lieferkette und Geheimnisse absichern, mit SBOM, Prüfungen und festgelegten Toren.
- Anwenden: nach einem Geheimnisleck in der richtigen Reihenfolge vorgehen.
- Beurteilen: Restrisiken benennen und die eigene Sicherheitslage gegenüber Fachleuten vertreten.
Arbeit am Jahresprojekt
Abschnitt betitelt „Arbeit am Jahresprojekt“In docs/sicherheit.md:
- Datenflussdiagramm mit Vertrauensgrenzen.
- STRIDE-Analyse für die zwei wichtigsten Abläufe, mit mindestens acht Bedrohungen.
- Missbrauchsgeschichten mit Gegenmaßnahme und Nachweis.
- Priorisierte Maßnahmenliste mit Status, Restrisiken mit Begründung.
- Ergebnis der gegenseitigen Prüfung samt Vereinbarung.
In der Anwendung umgesetzt:
- Anmeldung über einen Identitätsanbieter mit PKCE und vollständiger Token-Prüfung.
- Autorisierung zentral, Mandanten- oder Besitztrennung nachweisbar.
- Protokollierung sicherheitsrelevanter Ereignisse ohne Personenbezug im Übermaß.
- Abhängigkeits-, Geheimnis- und Image-Prüfung eingerichtet, mit festgelegten Toren.
Passende Übungen
Abschnitt betitelt „Passende Übungen“- Aufgabe - STRIDE-Analyse des eigenen Projekts
- Aufgabe - OIDC-Anmeldung umsetzen und Token prüfen
- Aufgabe - Lieferkette prüfen und Fund beheben
- Aufgabe - Gegenseitige Sicherheitsprüfung mit Bericht