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:
- URL — muss HTTPS sein. Selbstsignierte Zertifikate werden abgelehnt.
- 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.
- Event-Typen — wählen Sie die gewünschten Ereignisse. Für den Einsatz im SOC empfehlen wir mindestens
incident.createdundincident.status_changed. - Filter (optional) — schränken Sie ein nach
severity(low,medium,high,critical),finding_type(z. B.aws_access_key,credit_card) odergroup(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
| Feld | Typ | Beispiel | Hinweise |
|---|---|---|---|
event | string | incident.created | Punktierter Event-Name. Versionsübergreifend stabil. |
event_id | string (ULID) | evt_01HX9F4A1Y... | Eindeutig pro Zustellversuch-Gruppe. Als Dedupe-Schlüssel verwenden. |
delivered_at | string (RFC 3339) | 2026-05-17T14:23:11Z | UTC. Kann bei Wiederholungen vom Erstellungszeitpunkt abweichen. |
incident.id | string (ULID) | inc_01HX9F4A1Y... | Stabile Kennung. Über alle Events eines Vorfalls identisch. |
incident.severity | enum | high | Einer von low, medium, high, critical. |
incident.finding_type | string | aws_access_key | Entspricht dem Detektor-Slug; siehe Detektoren-Referenz. |
incident.user.email | string | alice@acme.com | Stammt aus dem SSO, nie vom Nutzer eingegeben. |
incident.user.group | string | engineering | SCIM-Gruppenname, falls vorhanden. |
incident.destination | string | chat.openai.com | Hostname des KI-Tools, an das der Prompt gerichtet war. |
incident.action | enum | block | Einer von monitor, anonymize, block. |
incident.redacted_preview | string | ... 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
highundcriticalin#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:
criticallöst eine Page aus,higherzeugt 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 überfinding_typeunduser.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.