Engineering

SOAR-Playbook-Design: Reaktive Datenflüsse, Conditional Branching und das Argument für Dry-Run

Die meisten SOAR-Playbooks sind brüchige Skripte in hübscher Verpackung. Die, die den Kontakt mit Produktion überleben, teilen vier Merkmale: einen visuellen Designer mit reaktivem Datenfluss, einen typisierten Node-Vertrag, echtes Conditional Branching und einen Dry-Run-Modus, der die Logik ausführt, ohne die Seiteneffekte auszulösen. So funktioniert jedes davon, und was sich ändert, wenn man sie hat.

Von Krasper Engineering 10. Juni 2026 10 Min. Lesezeit
SOAR Playbook Design — hero

TL;DR. Ein SOAR-Playbook soll die Codifizierung dessen sein, was die:der beste Analyst:in um 3 Uhr morgens tut. In der Praxis driften die meisten Playbooks in einen von zwei Failure-Modes: zu starr, um die Variation echter Incidents zu handlen, oder so generisch, dass sie nichts Substanzielles automatisieren. Vier Design-Entscheidungen, nämlich reaktiver Datenfluss, typisierter Node-Vertrag, First-Class Conditional Branching und ein nicht-destruktiver Dry-Run-Modus, trennen Playbooks, die genutzt werden, von Playbooks, die jedes Quartal neu geschrieben werden.

Das Versprechen von Security-Orchestrierung ist einfach: Codifiziere das Runbook, sodass es bei einem Alert automatisch feuert, lass die Plattform die mechanische Arbeit übernehmen und gib der:dem Analyst:in Raum zum Denken. Die Realität ist unordentlicher. Echte Incidents passen nicht zum Runbook, weil echte Incidents variieren. Dieselbe Phishing-Kampagne erreicht drei User mit leicht unterschiedlichen Mail-Headern; die Hälfte der URLs ist bei Triage-Beginn schon neutralisiert; das EDR hat zwei Endpoints geflagged, aber nicht den dritten; der:die User:in im Finance-Team hat Approver-Rechte, die das Containment-Kalkül ändern.

Ein Playbook, das diese Variation nicht auffangen kann, ist im Grunde nur ein Skript, und ein Skript, das sich nicht sicher ändern lässt, wird in dem Moment zur Belastung, in dem es Produktivsysteme anfasst. Genau deshalb verlieren so viele SOAR-Projekte nach dem ersten Jahr still das Vertrauen des Teams.

Dieser Beitrag geht durch die vier Design-Entscheidungen, die darüber entscheiden, ob ein Playbook unter echtem Traffic hält oder am Ende umgangen wird. Sie stammen aus dem Bau des Krasper-Suite-Playbook-Designers. Die Rahmung ist technisch, die Beispiele sind absichtlich konkret.

Inhalt

  1. Warum die meisten Playbooks verfallen
  2. Reaktiver Datenfluss: warum der Editor zählt
  3. Der Node-Vertrag: typisierte Ports, explizite Failure-Modes
  4. Conditional Branching als First-Class-Primitive
  5. Durchgespieltes Beispiel: Phishing-Response, end-to-end
  6. Dry-Run-Mechanik: Testen ohne Sprengradius
  7. Was sich ändert, wenn man alle vier hat

1. Warum die meisten Playbooks verfallen

Das Verfalls-Muster ist über Organisationen hinweg konsistent. Ein Playbook wird gegen ein sauberes mentales Modell des Incidents geschrieben. Es läuft gut in der Demo, gut in der ersten Woche, gut bei den Incidents, die wie der aussehen, den die:der Autor:in im Kopf hatte.

Dann kommen die Variationen.

Der Mail-Header-Parser wurde für das Cloud-Mail-Gateway geschrieben und bricht bei der On-Prem-Appliance. Die IP-zu-Asset-Enrichment liefert unknown für Cloud-Workloads. Der Containment-Schritt nimmt an, dass Endpoint-Isolation funktioniert, was sie tut, bis das Gerät offline ist. Der Notification-Schritt nimmt an, dass die:der Channel-Responder:in on call ist, was um 3 Uhr morgens am Samstag nicht der Fall ist.

Jede Variation produziert eine Exception. Exceptions sammeln sich als aufgesetzte Conditional-Logik, tief verschachtelt, zunehmend nicht mehr reviewbar. Nach sechs Monaten ist das Playbook eine 2.000-Zeilen-YAML-Datei, die ein:e Engineer versteht und nur ungern modifiziert. Das Team beginnt, drumherum zu arbeiten.

Die Wurzel ist selten die Logik selbst. Es ist die Repräsentation. Ein Playbook als lineares Skript mit wachsenden Conditionals zu repräsentieren ist eine Repräsentation, die ihre eigene Evolution bekämpft. Ein Playbook als typisierter gerichteter Graph mit reaktivem Datenfluss tut das nicht.

2. Reaktiver Datenfluss: warum der Editor zählt

Der visuelle Editor wird oft als UX-Spielerei abgetan. Ist er nicht. Er ist eine Forcing-Function für ein spezifisches Datenfluss-Modell, das lineare Skripte nicht erzwingen.

In einem reaktiven Node-Graph-Editor deklariert jede Node ihre Inputs und Outputs als benannte, typisierte Ports. Verbindungen zwischen Nodes sind explizit. Die Runtime läuft den Graphen ab, evaluiert eine Node, sobald alle ihre Inputs erfüllt sind, propagiert die Outputs zu Downstream-Nodes. Es gibt keinen versteckten State; der Graph ist das Programm.

   ┌──────────────┐       ┌──────────────────┐       ┌──────────────┐
   │  Alert       │       │  Enrich asset    │       │  Severity    │
   │  ingress     ├──────▶│                  ├──────▶│  classifier  │
   │              │       │  in: ip          │       │              │
   │  out: alert  │       │  out: asset      │       │  out: level  │
   └──────────────┘       └──────────────────┘       └──────┬───────┘
                                                            │
                          ┌─────────────────────────────────┴───┐
                          │                                     │
                          ▼                                     ▼
                   ┌────────────┐                       ┌────────────┐
                   │  Contain   │                       │  Notify    │
                   │  (high)    │                       │  (medium)  │
                   └────────────┘                       └────────────┘
Reaktiver Node-Graph: typisierte Ports, explizite Edges

Was diese Repräsentation gegenüber einem Skript bringt:

  • Der Graph ist der Vertrag. Ein:e Reviewer:in kann ihn lesen, und ein:e Junior kann eine Node ändern, ohne das Ganze verstehen zu müssen.
  • Seiteneffekte bleiben lokalisiert. Eine Node, die Produktion anfasst, ist eine einzelne, identifizierbare Form auf dem Canvas; die Nodes drumherum sind reine Transformationen.
  • Das Diffen wird strukturell. Eine Änderung am Graphen ist eine Änderung an einer definierten Menge von Nodes und Edges, nicht ein Gewirr verschobener Indentation in einem Tausend-Zeilen-Skript, und deshalb sind Pull-Requests gegen Playbooks tatsächlich reviewbar.

Der reaktive Teil zählt, weil der Editor den aktuellen Daten-State, der durch jeden Port fließt, anzeigen kann, während die:der Autor:in das Playbook gegen einen Sample-Alert baut. Die:der Autor:in sieht in Echtzeit, welche Form asset hat, wenn es den Classifier erreicht, nicht die Form, die die Doku behauptet. Die meisten Playbook-Bugs entstehen durch Drift zwischen dokumentierten und tatsächlichen Daten-Shapes; reaktives Feedback kollabiert diese Lücke.

3. Der Node-Vertrag: typisierte Ports, explizite Failure-Modes

Die Graph-Metapher bringt nur etwas, wenn die Nodes darin einem engen Vertrag folgen. Unser Node-Vertrag hat vier Pflichteigenschaften.

Erstens: Inputs und Outputs sind typisiert. Jeder Port hat ein deklariertes Schema, und die Runtime weigert sich, einen Port, der IpAddress ausgibt, mit einem zu verbinden, der EmailMessage erwartet. Das klingt offensichtlich, aber die Hälfte aller Playbook-Failures, die wir in Audits gesehen haben, geht auf Runtime-Type-Mismatches zurück, die der Editor zur Design-Time hätte abfangen können.

Zweitens: Failure-Outputs sind explizit. Jede Node, die ein externes System anfasst, hat einen success-Port und einen error-Port, und der Error-Port ist Pflicht: lässt die:der Autor:in ihn unverdrahtet, flaggt der Editor die Node als unvollständig. Von allen Entscheidungen hier hat diese die meisten Produktiv-Incidents verhindert, denn sie zwingt dazu, den Unhappy-Path schon zur Design-Time zu bedenken statt erst nach der ersten 2-Uhr-morgens-Page.

Drittens: Nodes sind idempotent per Default. Side-Effect-Nodes tragen einen automatisch generierten Idempotency-Key, abgeleitet aus Playbook-Execution-ID und Node-ID, sodass ein Replay derselben Execution gegen dieselbe Node auf Runtime-Ebene dedupliziert. Autor:innen müssen nie darüber nachdenken; die Plattform garantiert es.

Viertens: Timeouts sind deterministisch. Jeder externe Call deklariert ein Timeout, und eine Node, die es überschreitet, routet zum Error-Port, statt die Execution hängen zu lassen. Es gibt keinen "ich warte für immer auf die EDR-API"-Zustand, weil die Runtime die Node vorher killt.

Eine Node, die alle vier Eigenschaften erfüllt, komponiert sauber. Eine, die keine davon erfüllt, ist genau die Art, wie Playbooks verfallen.

4. Conditional Branching als First-Class-Primitive

Die meisten frühen SOAR-Produkte behandelten Conditionals als Nachgedanken, eine Node mit einem "if"-Ausdruck, Output zu einer einzelnen Downstream-Node geroutet. Das funktioniert für ein oder zwei Branches. Bei Scale kollabiert es.

Ein First-Class-Branching-Primitive sieht so aus:

  • Eine switch-Node mit N deklarierten Outputs, jeder durch ein typisiertes Prädikat über den Inputs bewacht.
  • Die Runtime evaluiert Prädikate in deklarierter Reihenfolge, routet zum ersten Match und kurzschließt den Rest.
  • Ein erforderlicher default-Output fängt alles, was auf kein Prädikat matcht. Der Editor weigert sich, das Playbook ohne ihn zu speichern.
  • Die Output-Ports des Switches tragen verengte Typen: downstream vom "Alert kommt vom Cloud-Mail-Gateway"-Branch verengt sich der alert-Port-Typ auf CloudMailAlert, und die Downstream-Nodes sehen nur noch die Felder, auf die sie sich verlassen können.
                     ┌────────────────────────────┐
                     │  switch on alert.source    │
                     │                            │
                     │  case cloud_mail  ────────┼──▶ cloud-mail subgraph
                     │  case onprem_mail ────────┼──▶ onprem subgraph
                     │  case api_gateway ────────┼──▶ api subgraph
                     │  default          ────────┼──▶ unknown-source handler
                     └────────────────────────────┘
Switch-Node mit Pflicht-Default und type-narrowed Outputs

Type Narrowing ist der Teil, der ein echtes Branching-Primitive von einem kosmetischen unterscheidet. Damit lässt sich jeder Subgraph gegen einen engeren Vertrag schreiben, und der Editor fängt Integrationsfehler zur Design-Time. Ohne ihn muss jeder Subgraph defensiv jedes Feld neu prüfen, genau der Failure-Mode, der Playbooks zu unlesbaren Conditionals aufbläht.

Der default-Branch ist nicht ohne Grund Pflicht. Unbehandelte Fälle in Produktion um 3 Uhr morgens produzieren keinen Wert. Entweder entscheidet das Playbook, was zu tun ist, oder es eskaliert explizit an einen Menschen. Beides ist akzeptabel; stilles Durchfallen ist es nicht.

5. Durchgespieltes Beispiel: Phishing-Response, end-to-end

Phishing ist das Standard-Beispiel, weil jedes SOC es betreibt und die Variations-Fläche groß ist. Das Skelett eines echten Playbooks sieht so aus:

[Alert ingress: mail-security signal]
        │
        ▼
[Parse headers + extract URLs/attachments]
        │
        ▼
[Enrich: sender reputation, domain age, URL reputation]
        │
        ▼
[switch: phishing_confidence]
   ├── high   → [Quarantine mail across all mailboxes]
   │              │
   │              ▼
   │           [Identify all recipients] ── for each ──▶
   │              ├── [User in privileged group?] ── yes ──▶ [Force password reset]
   │              │                                 └── no  ──▶ [Notify user via approved channel]
   │              ▼
   │           [Endpoint check: any URL clicked?] ── yes ──▶ [Isolate endpoint + open IR ticket]
   │
   ├── medium → [Quarantine for sender, flag for analyst review]
   │
   ├── low    → [Tag for trend analysis, no user impact]
   │
   └── default → [Hold for analyst, no automatic action]
Phishing-Response-Playbook: Skelett

Ein paar Dinge zu dieser Form.

Der Enrichment-Schritt ist eine Node, nicht drei; die Plattform handlet Parallel-Fan-Out intern. Die:der Autor:in schreibt keinen Concurrency-Code; sie:er deklariert, dass der Classifier sender_reputation, domain_age und url_reputation braucht, und die Runtime löst die Dependencies parallel auf, wo sie kann.

Die "for each recipient"-Schleife ist eine einzelne Iterator-Node mit angehängtem Subgraph. Der Subgraph ist selbst reviewbar, diffbar, isoliert testbar. Die:der Autor:in schreibt keine Schleifen; sie:er deklariert "dieser Subgraph läuft einmal pro Empfänger:in."

Der Privileged-User-Check ist ein Switch mit zwei Outcomes, beide explizit behandelt. Es gibt kein implizites Durchfallen, bei dem ein:e privilegierte:r User:in versehentlich den Standard-Notification-Pfad bekommt.

Der Endpoint-Isolation-Schritt hat einen Error-Port (oben nicht gezeigt), der zu einem Analyst-Alert routet, falls Isolation fehlschlägt, etwa weil der Endpoint offline ist. Das Playbook nimmt nie stillschweigend an, dass seine Containment-Aktion geglückt ist.

6. Dry-Run-Mechanik: Testen ohne Sprengradius

Das einzelne Feature, das entscheidet, ob Engineers tatsächlich an Playbooks iterieren, ist Dry-Run.

Ein Dry-Run führt den gesamten Graphen gegen einen echten oder gesampleten Alert aus, läuft jede Node ab, evaluiert jedes Prädikat, propagiert jeden Wert, aber ruft die Side-Effect-APIs nicht auf. Nodes, die Mail quarantänen, Endpoints isolieren, Passwort-Resets erzwingen oder Tickets öffnen würden, emittieren stattdessen einen Record, was sie getan hätten, und reichen den synthetischen Success-Output weiter.

Das Implementierungsmuster ist einfach: jeder Side-Effect-Adapter checkt am Eintritt ein execution_mode-Flag. Im live-Modus ruft er die Upstream-API. Im dry_run-Modus liefert er eine deterministische synthetische Response zurück, die dem Schema eines echten Success entspricht, und schreibt den intendierten Call in den Execution-Trace.

text
┌────────────────────────────────────┐
│ Side-effect adapter                │
│                                    │
│ if mode == "dry_run":              │
│   record_intended_call(args)       │
│   return synthetic_success(args)   │
│                                    │
│ else:                              │
│   call_real_api(args)              │
└────────────────────────────────────┘
Side-Effect-Adapter: mode-aware Einstiegspunkt

Die Vorteile kumulieren:

  • Autor:innen können ein Playbook gegen den tatsächlichen Phishing-Alert der vergangenen Woche laufen lassen und den vollen Execution-Trace sehen, bevor sie die Änderung pushen.
  • CI kann bei jeder Änderung einen Korpus historischer Alerts gegen das neue Playbook abspielen und den Build fehlschlagen lassen, wenn eine Execution unerwartet von der Vorversion abweicht.
  • Incident-Retros können den exakten Alert gegen das aktuelle Playbook abspielen, um zu bestätigen, dass der Fix greift.

Eine Design-Disziplin ist nötig: Dry-Run-Integrität ist eine Plattform-Garantie, keine Per-Node-Garantie. Jeder Side-Effect-Adapter, der ausgeliefert wird, muss das Mode-Flag respektieren, und die Runtime erzwingt es an der Adapter-Grenze, nicht im Ermessen der:des Autor:in. Ein einzelner Adapter, der Dry-Run ignoriert und stillschweigend Produktion ruft, macht den gesamten Modus zur Belastung. Adapter-Test-Suites verifizieren beide Modi bei jedem Release.

7. Was sich ändert, wenn man alle vier hat

Reaktiver Datenfluss, der typisierte Node-Vertrag, First-Class-Branching und Dry-Run helfen jeder für sich, aber zusammen verändern sie das Bild. Die Verschiebung zeigt sich an zwei Stellen, die man tatsächlich messen kann.

Die erste ist die Mean Time to Respond. Wenn das Playbook Variation nativ handhabt, verschwinden die manuellen Handoff-Schritte, die zuvor die Ausführung gegated haben. Für ein Phishing-Playbook der obigen Form beobachten wir über Betriebsumgebungen hinweg typischerweise, dass der automatisierbare Teil der Triage von mehreren Minuten, wenn ein Mensch ihn treibt, auf Sekunden fällt, wenn er end-to-end läuft, weil die:der Analyst:in die frühe Klassifizierungsentscheidung nicht mehr von Hand treffen muss. Die verbleibende Menschen-Zeit landet auf den wirklich mehrdeutigen Fällen, die das Playbook absichtlich eskaliert, und genau dorthin gehört Analyst:innen-Aufmerksamkeit ohnehin.

Die zweite ist die Mean Time to Change, die Metrik, die die meisten Teams überhaupt zu messen vergessen. Ein Playbook, dessen sichere Modifikation eine Woche dauert, ist ein Playbook, das das Team still vermeidet zu ändern. Mit Dry-Run und strukturellen Diffs schrumpft diese Loop typischerweise von Tagen auf unter eine Stunde: Graph editieren, gegen einen Korpus dry-runnen, strukturellen Diff reviewen, shippen. Die Folge ist, dass das Playbook damit Schritt hält, wie sich Angriffe tatsächlich entwickeln, statt immer weiter zurückzufallen.

Die Zahlen hängen stark von der Baseline und davon ab, wie gut der umgebende Stack integriert ist. Wer ein einzelnes Before/After-Vielfaches zitiert, verkauft eine Folie, misst kein Outcome. Über die von uns beobachteten Umgebungen bleibt die Richtung stabil, auch wenn die Größe des Effekts es nicht tut.

Abschluss

Ein SOAR-Produkt steht und fällt mit seinem Autoring-Substrat, nicht mit den Playbooks, die zufällig mitgeliefert werden: Editor, Node-Vertrag, Branching-Primitives und Dry-Run-Garantie sind das, worauf es ankommt. Die Playbooks sind bloß, wie dieses Substrat genutzt wird.

Ist das Substrat solide, entwickeln sich die Playbooks. Ist es das nicht, ossifizieren die Playbooks, das Team arbeitet drumherum, und das Investment hört stillschweigend auf, Wert zurückzuliefern.

Der nächste Beitrag in dieser Serie geht eine Schicht tiefer, in die Runtime selbst: wie Executions persistiert werden, wie Partial Failures recovered werden und warum das Execution-Log das wichtigste Audit-Artefakt der Plattform ist.

Weiterlesen

  • NIST SP 800-61r3: Computer Security Incident Handling Guide
  • MITRE D3FEND: Detection, Denial, Disruption Framework
  • Eric Evans: Domain-Driven Design, Kapitel zu Intention-Revealing Interfaces
Bereit, Ihre
Unternehmensinfrastruktur abzusichern?

Vereinbaren Sie ein technisches Briefing. Kein Sales-Pitch, nur Architekten und Ihr Team.