Zum Inhalt springen
Categoria: OPSEC9 Min. Lesezeit

STRIDE Threat Modeling im Sprint: Vollstaendiges Beispiel an einem Microservice

Por Lucas Andrade ·

Wie wir STRIDE auf einen echten Payments-Microservice innerhalb eines zweiwoechigen Sprints anwenden, mit DFD, priorisierten Bedrohungen und konkreten Mitigations.

STRIDE Threat Modeling im Sprint: Vollstaendiges Beispiel an einem Microservice
In diesem Artikel

Threat Modeling stirbt in einer Schublade, wenn es zu einem vierstundigen Meeting ohne Owner und einem 40-seitigen PDF wird, das niemand wieder oeffnet. Bei Basilisk OffSec verdrahten wir STRIDE in zweiwochige Sprints und nehmen einen Payments-Microservice als Versuchskaninchen: ein Backend-Dev, ein SRE, ein Offensive-Researcher, 90 Minuten zum Kickoff und 30 Minuten Review mitten im Sprint. Das Ergebnis ist kein Dokument, sondern 12 Jira-Issues mit verifizierbaren Mitigationen. Dieser Beitrag zeigt genau, wie wir es am payments-api-Service gefahren haben, der PSP-Webhooks empfaengt und mit Postgres, Redis und einem KMS spricht, und wie jeder STRIDE-Buchstabe im selben Sprint zu einem echten Patch im Code wurde statt zu einem Finding, das in einem Wiki altert.

Warum Threat Modeling in der Schublade stirbt#

Der Fehlermodus ist vorhersehbar: ein Big-Bang-Workshop produziert ein erschopfendes Dokument, das Dokument wird nie in Arbeit umgesetzt, und sechs Wochen spaeter ist die Architektur ohnehin daran vorbeigedriftet. Die Losung ist, Threat Modeling klein, wiederkehrend und ergebnisgebunden zu machen. Wir deckeln den Kickoff auf 90 Minuten, beschraenken den Scope auf einen Service und seine unmittelbaren Trust-Boundaries und verlangen, dass jede Bedrohung den Raum als verfolgtes Issue mit Akzeptanzkriterium verlaesst, nicht als Absatz. Der Offensive-Researcher haelt die Session ehrlich, indem er fragt "wie wurde ich das tatsaechlich ausnutzen" statt "ist das theoretisch schlecht". Time-Boxing erzwingt Priorisierung: man modelliert zuerst die Flows, die eine Trust-Boundary kreuzen, denn dort operieren echte Angreifer, und akzeptiert, dass eine kurzere Session jeden Sprint eine heroische schlaegt, die einmal passiert und verrottet.

Das DFD und die Trust-Boundaries#

Vor dem Modellieren zeichneten wir das Data Flow Diagram in draw.io mit vier Elementtypen: externe Entitaeten (der PSP, das Frontend), Prozesse (payments-api, worker-reconciliation), Datastores (Postgres tx_db, Redis idempotency_cache) und die Flows dazwischen. Wir markierten die Trust-Boundaries explizit: Internet, dann Cloudflare, dann Ingress, dann internes Mesh, dann KMS. Das Diagramm muss nicht schoen sein, es muss korrekt sein, und in 25 Minuten hatten alle abgezeichnet. Wer je ein Lab gebaut hat, weiss, dass ein falsches Diagramm zu falschen Tests fuhrt, wie in Web-Pentest von Null: Ein Sicheres Lab mit DVWA, Juice Shop und Burp Suite Bauen behandelt. Dieselbe Regel hier: zeigt Ihr Webhook-Flow die HMAC-Validierung nicht vor dem JSON-Parse, modellieren Sie den Service in Ihrem Kopf, nicht den in Produktion.

Spoofing: beweisen, wer wirklich aufruft#

Spoofing tauchte zuerst am PSP-zu-payments-api-Flow auf. Der Webhook kam mit einem X-Signature-Header, aber die Verifikation lief nach json.loads(body), was ein Parser-Differential-Fenster liess, in dem ein gefaelschtes Event teilweise verarbeitet werden konnte, bevor die Signaturpruefung scheiterte. Der Fix war, den HMAC-SHA256 mit einem KMS-rotierten Schluessel zu validieren, bevor der Body ueberhaupt beruhrt wird, mit konstanter Zeit ueber hmac.compare_digest, um ein Timing-Orakel zu vermeiden. Wir pinnten zudem den akzeptierten Signaturalgorithmus, statt einem client-gelieferten Header zu vertrauen, und schlossen so einen Algorithm-Confusion-Downgrade. Die generelle Lehre: authentifizieren Sie die Nachricht, bevor Sie sie parsen, und lassen Sie Identitaet nie durch denselben nicht vertrauenswuerdigen Input behaupten, dem Sie gleich vertrauen. Jede externe Entitaet im DFD bekam dieselbe Frage, und dort haeuften sich die Spoofing-Findings.

Tampering: Integritaet ruhender und fliessender Daten#

Tampering tauchte in Redis auf: der Idempotency-Cache hatte keine feste TTL oder Signatur, sodass ein Angreifer mit internem Netzzugang Eintraege setzen und Charge-Replays ausloesen oder legitime unterdruecken konnte. Wir fuegten ein Namespace-Prefix plus einen kurzen HMAC auf den Key hinzu und erzwangen eine ACL mit requirepass und TLS auf Redis 7, sodass der Datastore keine weiche Trust-Zone mehr ist, nur weil er im Mesh sitzt. Auf der Leitung verlangten wir mutual TLS zwischen API und Worker, sodass ein kompromittierter Sidecar Reconciliation-Nachrichten nicht heimlich umschreiben kann. Die Modellierungsfrage fuer jeden Flow und Datastore war unverblumt: sasse ein Angreifer hier, was koennte er aendern und wurden wir es bemerken. Wo die Antwort "heimlich aendern" war, fuegten wir Integritaetsschutz hinzu, und wo sie "wurden wir nicht bemerken" war, das Logging, von dem der naechste Buchstabe abhaengt.

Repudiation: Handlungen beweisbar machen#

Repudiation behandelten wir mit einer Append-only-audit_log-Tabelle mit verketteten Hashes, sodass jeder Datensatz auf den vorherigen committet und eine stille Loschung oder Aenderung die Kette bricht. Das ist dasselbe tamper-evidente Muster, das wir beim Dokumentieren von Pivoting durch segmentierte Netze in Pivoting mit Chisel und Ligolo-ng: Segmentierte Netzwerke im Pentest-Lab nutzen, hier auf Geldbewegung statt Operator-Aktionen angewandt. Wir loggen den authentifizierten Principal, die Request-ID, den Vorher-Nachher-State-Hash und einen monotonen Zeitstempel fuer jede Charge, Erstattung und Reconciliation-Entscheidung. Der Punkt ist nicht, Logs um ihrer selbst willen zu sammeln, sondern nach einem Vorfall genau beweisen zu koennen, wer was in welcher Reihenfolge tat, ohne sich auf eine mutable Tabelle zu verlassen, die ein Angreifer mit DB-Zugang umschreiben koennte. Repudiation-Kontrollen sind vorne billig und hinterher fast unmoglich zu rekonstruieren.

Information Disclosure: die schwerste Kategorie#

Information Disclosure war die schwerste Kategorie mit acht Findings. Stack Traces leckten ueber FastAPI-500er in einer Staging-Umgebung, die nach Prod gespiegelt war, Secrets tauchten in einer /debug-Route hinter einem Magic-Header auf, den ein Praktikant 2024 zu entfernen vergass, und der Prometheus-/metrics-Endpunkt exponierte Labels mit card_bin. Wir fixten es mit Middleware, die nur {error_id, code} an den Client serialisiert, killten die Debug-Route und wandten eine relabel_config in Prometheus an, um sensible Labels beim Scrape zu droppen. Um die Wirkung fur das Team konkret zu machen, demonstrierten wir einen POC aequivalent zu einer SSRF, die Cloud-Metadata zieht, eine Ubung dokumentiert in SSRF Entmystifiziert: Cloud-Metadata in einem Lokalen AWS-Lab Ausnutzen. Ein echtes Token aus einem Metadata-Endpunkt zu sehen aenderte den Raum von "das Log ist okay" zu "an der Grenze alles redigieren".

Denial of Service: mehr als Rate-Limiting#

Denial of Service behandelten wir nicht nur als Rate-Limiting. Wir kartierten algorithmische Amplifikation: ein /search-Endpunkt akzeptierte eine client-gelieferte Regex und traf Postgres mit LIKE %term%, was sowohl ein ReDoS- als auch ein Full-Scan-Risiko ist. Wir ersetzten es durch tsvector plus GIN-Index und ein 64-Zeichen-Cap auf den Term, was eine unbegrenzte Query in eine begrenzte verwandelt. Wir fuegten einen per-API-Key-Token-Bucket in Envoy mit 100 rps und Burst 200 hinzu und einen Circuit Breaker auf dem KMS-Client mit pybreaker, weil das managed KMS eine Quote von 1200 Ops pro Sekunde je Schluessel hat und wir im Januar bereits einen 14-minutigen selbstverschuldeten Vorfall verursachten. DoS-Modellierung fragt, wo ein kleiner Input unverhaeltnismaessige Arbeit erzeugt, und jeder solche Punkt bekam ein Bound, einen Cache oder einen Breaker, sodass ein einzelner Aufrufer den Service nicht lahmlegen kann.

Elevation of Privilege: den Abschluss bilden#

Elevation of Privilege schloss die Liste. Der interne API-JWT nutzte HS256 mit einem einzigen Secret, das ueber sechs Services geteilt war, was bedeutet, dass jeder einzelne kompromittierte Service Tokens praegen kann, die alle anderen akzeptieren. Wir migrierten zu RS256 mit per-Service-Schluesseln im KMS, audience-spezifischen Claims, sodass ein fur einen Service gepraegtes Token von einem anderen abgelehnt wird, und voller exp-, nbf- und iss-Validierung in einer einzigen Middleware, ausgeliefert als interne Library basilisk-authz==2.3.0. Die Zentralisierung in einer auditierten Library entfernte die Drift, in der jeder Service leicht anders validierte, was selbst ein Elevation-Pfad ist. Die Modellierungsfrage war simpel: ist diese Komponente voll ubernommen, was gewinnt der Angreifer anderswo, und die Shared-Secret-Antwort war "alles", also wurde es der Fix mit hochster Prioritaet im Batch.

Bedrohungen in verfolgte Issues verwandeln#

Am Ende des Sprints wurde jeder Punkt ein Issue mit dem Titel STRIDE-<Buchstabe>-<Nr>: <Bedrohung> und einem mitigation:<status>-Tag, denn eine Bedrohung ohne Ticket ist eine Bedrohung, die nicht gefixt wird. Von den 12 aufgeworfenen Bedrohungen gingen 9 als gemergte PRs im selben Sprint raus, 2 wurden als Restrisiko mit dokumentiertem 90-Tage-Review akzeptiert, und 1 wurde zu einem Epic, um das Webhook-Modul zu refaktorieren. Der Gesamtaufwand war etwa 4 Stunden verteilte Meetings plus Code-Arbeit, die ohnehin auf dem Sprint-Board war. Die Disziplin, die das haelt, ist, die Modellierungssession nicht zu schliessen, bis jede aufgeworfene Bedrohung einen Owner, einen Status und ein verifizierbares Akzeptanzkriterium hat, sodass das Ergebnis ein abbaubarer Backlog ist statt eines ignorierbaren Reports.

Tooling und CI-Gates, die es ehrlich halten#

Wir stuetzten das manuelle Modellieren mit Automatisierung, sodass Regressionen eine gefixte Bedrohung nicht stumm wieder offnen. SAST laeuft mit custom Semgrep-Regeln, die die spezifischen Fehler kodieren, die wir fanden, etwa ein HMAC-Check nach einem Parse, und SCA laeuft mit osv-scanner gegen den Dependency-Baum. Beide sind blockierende Gates fur High- und Critical-Findings in der Pipeline, sodass ein Pull Request, der eine modellierte Schwaeche wieder einfuhrt, die CI bricht statt auszuliefern. Wir halten die Semgrep-Regeln im selben Repo wie den Service, versioniert neben dem Code, den sie schutzen, und reviewen sie, wenn ein neues STRIDE-Finding ein automatisch fangbares Muster nahelegt. Automatisierung ersetzt die menschliche Session nicht, sie macht sie kumulativ: jeden Sprint findet das manuelle Modell die neuen Probleme und die Regeln sorgen dafur, dass der letzte Sprint gefixt bleibt.

FAQ: reichen 90 Minuten wirklich?#

Fur einen Service mit klarer Grenze ja, und die Beschraenkung ist ein Feature, kein Kompromiss. Ein enger Rahmen zwingt das Team, zuerst die Flows zu modellieren, die eine Trust-Boundary kreuzen, wo die ausnutzbaren Bugs tatsaechlich leben, und die geringwertige Analyse rein interner Hilfsfunktionen aufzuschieben. Ist ein Service so gross, dass 90 Minuten seine grenzuberschreitenden Flows nicht abdecken, ist das ein Signal, dass der Service zu viel tut und geteilt werden sollte, nicht dass die Session vier Stunden laufen sollte. Die wiederkehrende Kadenz macht den kurzen Rahmen tragfaehig: was Sie diesen Sprint verpassen, fangen Sie naechsten Sprint, gegen eine Architektur, die nur zwei Wochen statt sechs Monate gedriftet ist.

FAQ: was, wenn wir keinen Offensive-Researcher haben?#

Man kann STRIDE ohne dedizierten Red-Teamer fahren, muss aber bewusst die adversariale Denkweise importieren, die der Researcher liefert, denn ein Raum voller Builder neigt dazu zu modellieren, wie das System funktionieren soll, statt wie es bricht. Weisen Sie pro Session eine Person zu, den Angreifer zu spielen, und halten Sie sie an konkrete Ausnutzung, indem Sie "gib mir den exakten Request, der das missbraucht" fragen statt "das koennte riskant sein" zu akzeptieren. Seeden Sie die Session mit einer Checkliste der sechs Buchstaben gegen jeden Flow und einer Bibliothek vergangener Findings, sodass die Fragen strukturiert statt improvisiert sind. Es ist weniger wirksam als ein echter Offensive-Spezialist, aber eine disziplinierte Angreiferrollen-Rotation plus die automatisierten Gates holt den grossten Teil des Werts zuruck und zuchtet den Sicherheitsinstinkt uber das ganze Team.

Fazit: eine gemeinsame Sprache, keine Audit-Checkliste#

STRIDE ist keine Audit-Checkliste, es ist eine gemeinsame Sprache zwischen Dev, SRE und Offensive, die Architektur in eine priorisierte Fix-Liste verwandelt. Das praktische Rezept: mit einem einseitigen DFD starten, die sechs Buchstaben gegen jeden Flow zwingen, der eine Trust-Boundary kreuzt, jede Bedrohung als Issue mit verifizierbarem Akzeptanzkriterium landen lassen und das Ganze mit SAST- und SCA-Gates stutzen, sodass gefixte Bedrohungen gefixt bleiben. Fahren Sie es jeden Sprint gegen einen kleinen Scope statt einmal gegen alles, halten Sie die Session mit einer Angreiferrolle ehrlich und messen Sie Erfolg an gemergten PRs statt an geschriebenen Seiten. Wird es diesen Sprint nicht zu Code, war es kein Threat Modeling, sondern Theater.

Related posts

Nenhum comentário ainda

Seja o primeiro a comentar.

Deixe seu comentário

Entre com sua conta Canverly para comentar. Você pode usar a mesma conta em qualquer site da rede.

Entrar com Canverly