Skip to main content

Webhooks (Asynchrone Webhooks)

FÜR AI-AGENTENChatGPTClaude

Architekturmuster für asynchrone, dienstübergreifende Interaktion (Event-driven Push), bei dem der Ereignisanbieter bei Eintritt eines Systemereignisses eine HTTP-POST-Anfrage mit Daten an die registrierte URL des Verbrauchers sendet.

1. Überblick über das Konzept und das Systemproblem

Die traditionelle Systemintegration durch ständiges Abfragen (Polling / Short Polling) leidet unter zwei sich gegenseitig ausschließenden Problemen:

  1. Hohe Reaktionslatenz (Latency): Wenn der Client alle 60 Sekunden auf Änderungen prüft, werden Daten im Durchschnitt mit einer Verzögerung von 30 Sekunden zugestellt.
  2. Enormer Verschwendung von Rechenressourcen: 99 % der HTTP-Anfragen liefern eine leere Antwort 304 Not Modified oder [], was die Datenbank und den Netzwerk-Stack unnötig belastet.

Webhooks (Rückrufe über HTTP) ändern das Paradigma von Pull zu Push. Anstatt zu fragen: "Gibt es neue Daten?", registriert der Consumer seine URL (Webhook Listener). Sobald beim Provider ein Ereignis auftritt (erfolgreiche Stripe-Transaktion, neuer GitHub-Commit, eingehende Telegram-Nachricht), sendet der Provider einen HTTP POST-Request mit JSON-Payload direkt an den Server des Abonnenten.

Традиційний Polling (Марнування ресурсів):
Клієнт  ---> GET /events?since=... ---> Сервер (Порожньо: [])
Клієнт  ---> GET /events?since=... ---> Сервер (Порожньо: [])
Клієнт  ---> GET /events?since=... ---> Сервер (Нова подія!)

Event-Driven Webhook (Миттєвий Push):
Провайдер подій (Stripe / GitHub / Telegram)
       |
       |  Подія: charge.succeeded (Event Payload + HMAC підпис)
       |  HTTP POST https://api.mysite.com/webhooks/stripe
       v
Споживач (Валідація підпису -> 200 OK -> Фонова черга RabbitMQ/Redis)

2. Architektonische Taxonomie und mentales Modell

Der Lebenszyklus eines Webhooks basiert auf drei architektonischen Säulen:

  1. Kryptografische Authentifizierung (HMAC Signature):
    • Der Provider generiert einen kryptografischen Hash aus dem Request-Body (raw body) unter Verwendung eines gemeinsamen geheimen Schlüssels (Webhook Secret) und fügt diesen in den Header ein (z. B. Stripe-Signature oder X-Hub-Signature-256).
    • Der Consumer berechnet denselben Hash und validiert ihn, wodurch das Risiko einer Request-Fälschung durch Angreifer ausgeschlossen wird.
  2. Idempotenz und Deduplizierung (Idempotency Engine):
    • Jedes Ereignis besitzt eine global eindeutige ID (evt_xxxxxxxxxx).
    • Der Consumer prüft atomar, ob die ID im Cache/DB vorhanden ist (z. B. via INSERT ... ON CONFLICT DO NOTHING oder Redis SET key NX EX 86400).
  3. Garantierte Zustellpolitik und Wiederholungen (Retry & Dead-Letter Queue):
    • Bei Nichtverfügbarkeit des Consumerservers wiederholt der Provider die Anfragen nach exponentieller Skala (Exponential Backoff mit Jitter): nach 5 Min., 30 Min., 2 Std., 12 Std.
    • Ereignisse, die nach maximaler Versuchsanzahl nicht verarbeitet werden konnten, werden in eine Dead-Letter Queue (DLQ) zur manuellen Analyse durch Ingenieure geleitet.

3. Technische Pipeline und interne Mechanik

Produktionsreifer Handler mit HMAC-Verifizierung (Node.js / Express / TypeScript)

[!IMPORTANT] Für die Signaturprüfung muss der unverarbeitete Request-Body (raw buffer) empfangen werden, nicht das geparste Objekt req.body. Andernfalls stimmt der Hash aufgrund von Unterschieden bei Leerzeichen und JSON-Schlüssel-Sortierung nicht überein.

import express, { Request, Response } from "express";
import crypto from "crypto";

const app = express();

// Отримання raw buffer для ендпоінтів вебхуків
app.post(
  "/api/v1/stripe-webhook",
  express.raw({ type: "application/json" }),
  async (req: Request, res: Response) => {
    const signature = req.headers["stripe-signature"] as string;
    const secret = process.env.STRIPE_WEBHOOK_SECRET!;

    if (!signature) {
      return res.status(400).send("Missing signature header");
    }

    // 1. Криптографічна валідація підпису через HMAC SHA-256
    const computedHmac = crypto
      .createHmac("sha256", secret)
      .update(req.body)
      .digest("hex");

    // Безпечне порівняння за фіксований час
    const isValid = crypto.timingSafeEqual(
      Buffer.from(signature),
      Buffer.from(computedHmac)
    );

    if (!isValid) {
      return res.status(401).send("Invalid signature");
    }

    const payload = JSON.parse(req.body.toString());
    const eventId = payload.id;

    // 2. Ідемпотентна перевірка дедуплікації
    const isNew = await redis.set(`webhook:processed:${eventId}`, "1", "NX", "EX", 86400);
    if (!isNew) {
      // Подію вже оброблено — повертаємо 200 OK без повторного виконання
      return res.status(200).json({ status: "already_processed" });
    }

    // 3. Відправка важкої роботи в чергу BullMQ
    await eventQueue.add("process_payment", payload);

    // 4. Миттєве підтвердження отримання
    return res.status(200).json({ received: true });
  }
);

4. Praktische Engineering-Szenarien in Production

01. Zahlungs-Gateway für AI SaaS (Stripe / Paddle / LiqPay)

Ein Nutzer zahlt ein Pro-Abo. Das Zahlungssystem generiert das Event invoice.payment_succeeded. Der Webhook aktualisiert sofort den Status des Nutzers in der Datenbank, setzt Token-Limits in Redis zurück und sendet eine Begrüßungs-E-Mail, sodass der Nutzer ohne Seitenneuladen weiterarbeiten kann.

02. Trigger autonomer CI/CD-Pipelines (GitHub Webhooks)

Ein Ingenieur führt git push auf den Branch main aus. GitHub sendet den Payload mit dem Commit-Hash an den eigenen Server (Coolify oder ArgoCD). Der Server überprüft das Secret, klont den geänderten Code, führt Unit-Tests in einem isolierten Docker-Container aus und aktualisiert den Production-Service gemäß Blue/Green-Strategie.

03. Asynchrone Sammlung von Inferenz-Ergebnissen schwerer AI-Modelle

Beim Aufruf von Video-Generierungsmodellen (Runway, Kling) oder LoRA-Training dauert die Anfrage mehrere Minuten. Statt eine offene HTTP-Verbindung aufrechtzuerhalten, übergibt der Client eine webhook_url. Sobald der GPU-Cluster die Generierung abgeschlossen hat, sendet er einen Webhook mit dem finalen S3-Link zum generierten Medium.


5. Fallstricke, typische Fehler und Sicherheit

  1. JSON-Parsing vor Signaturverifizierung: Wenn vor der Prüfung der Signatur das Standard-Middleware express.json() ausgeführt wurde, kann der Serializer die Reihenfolge der Felder, die Unicode-Zeichen-Escaping oder die Zahlenformatierung ändern. Dies führt zu einer Inkonsistenz der HMAC-Prüfsumme und Ablehnung als Invalid Signature.
  2. Synchrones Blockieren der Ausführung (Webhook Deadlock): Die Ausführung schwerer Transaktionen oder langlaufender HTTP-Anfragen an externe APIs innerhalb des Webhook-Handlers führt garantiert zu Timeouts auf Provider-Seite. Geben Sie 200 OK sofort nach Validierung der Signatur und Speichern in die Queue zurück.
  3. Fehlende Validierung von Zeitstempeln (Replay Attacks): Ein Angreifer, der einen legitimen signierten Webhook-Request abgefangen hat, kann ihn Stunden später erneut senden. Provider fügen der Signatur einen Zeitstempel (timestamp) hinzu. Prüfen Sie, dass die Differenz zwischen Anfragezeit und Systemuhr des Servers ein zulässiges Fenster (z. B. 5 Minuten) nicht überschreitet.
/ Häufig gestellte FragenSchema.org FAQPage

FAQ: Webhooks (Asynchrone Webhooks)

Die klassische Zeichenkettenvergleichung mit den Operatoren == oder === bricht beim ersten abweichenden Zeichen ab. Ein Angreifer kann die Antwortverzögerung des Servers mit Nanosekundengenauigkeit messen (Timing Attack) und so gültige Signatur-Hashes byteweise erraten. Die Funktion timingSafeEqual führt den Vergleich in einer festen Zeitdauer aus und gewährleistet so kryptografische Sicherheit.
/ Interne Links
Alle Begriffe