Webhooks und SIEM-Integration

Übertragen Sie Zeuslock-Incident- und Policy-Events an Ihr SIEM, Chat oder On-Call-Tooling über signierte Webhooks und vorgefertigte Konnektoren für Slack, Splunk, Sentinel und PagerDuty.

Was Zeuslock sendet und wann

Zeuslock gibt zwei Familien von Ereignissen an externe Systeme aus: Incident-Events (eine Nutzeraktion, die eine Policy ausgelöst hat) und Policy-Events (eine Änderung, die ein Operator in der Konsole vorgenommen hat). Webhooks sind HTTPS-POST-Anfragen mit einem JSON-Body und einem HMAC-SHA256-Signatur-Header. Jeder konfigurierte Endpoint erhält nur die Event-Typen, die Sie abonniert haben, optional gefiltert nach Schweregrad, Detektionstyp oder Benutzergruppe.

Folgende Event-Typen werden derzeit ausgegeben:

  • incident.created — die Erweiterung, der Desktop-Agent oder die CLI hat soeben eine neue Detektion ausgelöst.
  • incident.status_changed — ein Operator hat den Status des Vorfalls geändert (offen, in Bearbeitung usw.).
  • incident.resolved — der Vorfall wurde geschlossen.
  • incident.false_positive — ein Operator hat die Detektion als False Positive markiert; nützlich, um eigene Dashboards nachzutrainieren.
  • policy.changed — eine Detektions- oder Reaktions-Policy wurde bearbeitet und veröffentlicht.
  • policy.dry_run_completed — ein Backtest im Trockenlauf wurde abgeschlossen; das Payload enthält den Diff zwischen aktueller und vorgeschlagener Policy.

Einen Endpoint konfigurieren

Öffnen Sie Operator-Konsole → Einstellungen → Webhooks → Endpoint hinzufügen. Sie werden nach folgenden Feldern gefragt:

  1. URL — muss HTTPS sein. Selbstsignierte Zertifikate werden abgelehnt.
  2. Secret — Zeuslock erzeugt eine zufällige hexadezimale Zeichenkette von 32 Byte Länge. Kopieren Sie sie sofort; sie wird nur einmal angezeigt und ausschließlich als Hash gespeichert.
  3. Event-Typen — wählen Sie die gewünschten Ereignisse. Für den Einsatz im SOC empfehlen wir mindestens incident.created und incident.status_changed.
  4. Filter (optional) — schränken Sie ein nach severity (low, medium, high, critical), finding_type (z. B. aws_access_key, credit_card) oder group (jede über SCIM synchronisierte Okta-/Azure-AD-Gruppe).

Legen Sie pro nachgelagertem System einen eigenen Endpoint an. Slack und Splunk auf derselben URL zu mischen macht spätere Retry-Analysen und Secret-Rotationen sehr unangenehm.

Aufbau des Payloads

Alle Events nutzen denselben Umschlag: event, event_id, delivered_at und ein typisiertes Objekt, dessen Schlüssel der Event-Familie entspricht (incident oder policy). Beispiel für incident.created:

{
  "event": "incident.created",
  "event_id": "evt_01HX9F4A1Y7QZ2C8K3J5W6N0PD",
  "delivered_at": "2026-05-17T14:23:11Z",
  "incident": {
    "id": "inc_01HX9F4A1Y7QZ2C8K3J5W6N0PD",
    "severity": "high",
    "finding_type": "aws_access_key",
    "user": {
      "email": "alice@acme.com",
      "group": "engineering"
    },
    "destination": "chat.openai.com",
    "action": "block",
    "redacted_preview": "Can you debug this AWS error? AKIA*** ..."
  }
}

Feldreferenz

FeldTypBeispielHinweise
eventstringincident.createdPunktierter Event-Name. Versionsübergreifend stabil.
event_idstring (ULID)evt_01HX9F4A1Y...Eindeutig pro Zustellversuch-Gruppe. Als Dedupe-Schlüssel verwenden.
delivered_atstring (RFC 3339)2026-05-17T14:23:11ZUTC. Kann bei Wiederholungen vom Erstellungszeitpunkt abweichen.
incident.idstring (ULID)inc_01HX9F4A1Y...Stabile Kennung. Über alle Events eines Vorfalls identisch.
incident.severityenumhighEiner von low, medium, high, critical.
incident.finding_typestringaws_access_keyEntspricht dem Detektor-Slug; siehe Detektoren-Referenz.
incident.user.emailstringalice@acme.comStammt aus dem SSO, nie vom Nutzer eingegeben.
incident.user.groupstringengineeringSCIM-Gruppenname, falls vorhanden.
incident.destinationstringchat.openai.comHostname des KI-Tools, an das der Prompt gerichtet war.
incident.actionenumblockEiner von monitor, anonymize, block.
incident.redacted_previewstring... AKIA*** ...Immer serverseitig geschwärzt. Das Roh-Secret verlässt den Endpunkt nie.

HMAC-Signatur prüfen

Jede Anfrage trägt einen Header X-Zeuslock-Signature der Form sha256=<hex>. Der Digest ist HMAC-SHA256(secret, raw_body), berechnet über die exakten Body-Bytes — bitte nicht erneut serialisieren. Verwerfen Sie alles, was nicht übereinstimmt, und verwerfen Sie alles, was älter als fünf Minuten ist, indem Sie delivered_at mit Ihrer Uhr vergleichen.

Python

import hmac, hashlib
from datetime import datetime, timezone

SECRET = b"ihr-32-byte-hex-secret"

def pruefen(raw_body: bytes, signature_header: str, delivered_at: str) -> bool:
    if not signature_header.startswith("sha256="):
        return False
    erwartet = hmac.new(SECRET, raw_body, hashlib.sha256).hexdigest()
    erhalten = signature_header.split("=", 1)[1]
    if not hmac.compare_digest(erwartet, erhalten):
        return False
    gesendet = datetime.fromisoformat(delivered_at.replace("Z", "+00:00"))
    return abs((datetime.now(timezone.utc) - gesendet).total_seconds()) < 300

Node.js

import crypto from "node:crypto";

const SECRET = "ihr-32-byte-hex-secret";

export function pruefen(rawBody, signatureHeader, deliveredAt) {
  if (!signatureHeader?.startsWith("sha256=")) return false;
  const erwartet = crypto
    .createHmac("sha256", SECRET)
    .update(rawBody)
    .digest("hex");
  const erhalten = signatureHeader.slice("sha256=".length);
  const a = Buffer.from(erwartet, "hex");
  const b = Buffer.from(erhalten, "hex");
  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) return false;
  const versatz = Math.abs(Date.now() - Date.parse(deliveredAt));
  return versatz < 5 * 60 * 1000;
}

Go

package webhook

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "strings"
    "time"
)

var secret = []byte("ihr-32-byte-hex-secret")

func Pruefen(rawBody []byte, sigHeader, deliveredAt string) bool {
    if !strings.HasPrefix(sigHeader, "sha256=") {
        return false
    }
    mac := hmac.New(sha256.New, secret)
    mac.Write(rawBody)
    erwartet := mac.Sum(nil)
    erhalten, err := hex.DecodeString(strings.TrimPrefix(sigHeader, "sha256="))
    if err != nil || !hmac.Equal(erwartet, erhalten) {
        return false
    }
    gesendet, err := time.Parse(time.RFC3339, deliveredAt)
    if err != nil {
        return false
    }
    return time.Since(gesendet).Abs() < 5*time.Minute
}

Verwenden Sie eine zeitkonstante Vergleichsfunktion (hmac.compare_digest, crypto.timingSafeEqual, hmac.Equal). Ein einfacher ==-Vergleich lässt das Secret über Zeit durch Timing-Analyse durchsickern.

Retries und Idempotenz

Liefert Ihr Endpunkt etwas anderes als einen 2xx-Code oder läuft das 10-Sekunden-Timeout ab, wiederholt Zeuslock mit folgendem Backoff: 1 Minute, 5 Minuten, 30 Minuten, 2 Stunden, 12 Stunden. Nach fünf aufeinanderfolgenden Fehlschlägen wird der Endpunkt automatisch ausgesetzt, in der Operator-Konsole erscheint ein Banner, und falls Slack verbunden ist, geht eine Slack-Warnung an Ihren Security-Kanal.

Jeder erneute Zustellversuch verwendet dieselbe event_id. Behandeln Sie diese ID als Primärschlüssel in Ihrem Zielspeicher, dann zählen Sie einen Incident nie doppelt, auch nicht bei einem Teilausfall. Body und Signatur sind zwischen Versuchen byte-identisch, sodass Sie die Verifizierung nicht für Retries gesondert behandeln müssen.

Wenn Sie eine Pipeline mit At-least-once-Semantik betreiben, etwa Kafka oder eine Logging-Queue, sollten Sie die event_id als Idempotenz-Schlüssel bis zum endgültigen Sink durchreichen. Speichern Sie zusätzlich delivered_at, um spät eintreffende Wiederholungen leicht von Originalereignissen zu unterscheiden.

Direkte Integrationen

Für die gängigen Ziele benötigen Sie keinen eigenen Receiver. Alle folgenden Integrationen finden Sie unter Einstellungen → Integrationen:

  • Slack — OAuth-Installation, Kanalauswahl, optionaler Schweregrad-Filter. Eine gängige Einrichtung: nur high und critical in #security-alerts, der Rest in #zeuslock-firehose.
  • Microsoft Teams — eingehende Webhook-URL einfügen. Verwenden Sie einen dedizierten Kanal; der Konnektor postet Adaptive Cards, die in einem allgemeinen Kanal schnell stören.
  • PagerDuty — Routing Key der Events API v2. Der Schweregrad wird direkt gemappt: critical löst eine Page aus, high erzeugt einen Incident ohne Page, niedrigere Stufen werden verworfen.
  • Splunk HEC — HEC-URL und Token, Sourcetype zeuslock:incident. Events kommen als JSON an; bauen Sie Dashboards über finding_type und user.group.
  • Microsoft Sentinel — Log-Analytics-Workspace-ID und Shared Key. Die Custom-Log-Tabelle heißt Zeuslock_Incidents_CL. Hunting-Queries und eine Analytics-Regel-Vorlage werden mit dem Konnektor ausgeliefert.
  • Generischer Webhook-Adapter — für alles andere (Datadog, Elastic, Chronicle, hauseigener Bus). Derselbe signierte Umschlag wie oben.

Endpunkt testen

Verwenden Sie die Schaltfläche Testereignis senden neben jedem Endpunkt in der Konsole. Sie löst ein synthetisches incident.created mit einer event_id, die mit evt_test_ beginnt, sodass Sie es aus Produktions-Dashboards herausfiltern können.

Wenn Sie es lieber aus der CI heraus skripten, rufen Sie die REST-API mit einem Operator-API-Token auf:

curl -X POST \
  -H "Authorization: Bearer $ZEUSLOCK_API_TOKEN" \
  https://api.zeuslock.ai/v1/webhooks/wh_01HX.../test

Häufige Fehler: TLS-Handshake-Probleme (prüfen Sie Ihre Zertifikatskette), 401 von Ihrem eigenen Auth-Proxy (tragen Sie die in der Konsole dokumentierten Zeuslock-Egress-IPs in die Allowlist ein) und Signatur-Mismatches, wenn ein Framework den Request-Body umschreibt. Parst Ihr Framework JSON, bevor Ihr Handler läuft, müssen Sie zuerst die rohen Bytes erfassen, sonst schlägt die HMAC-Prüfung fehl.