Webhooks erklärt: Signierte Payloads und Retry-Logik

Backend2026-10-25TryQuickToolBox

Sie haben eine API entwickelt und müssen nun andere Dienste benachrichtigen, wenn Ereignisse eintreten. Sie entscheiden sich für Webhooks: HTTP-Callbacks, die Daten an Abonnenten-URLs pushen. Bald stehen Sie jedoch vor zwei kritischen Fragen: Wie stellen Sie sicher, dass die Payload nicht manipuliert wurde? Und was passiert, wenn der empfangende Server nicht erreichbar ist oder einen Fehler zurückgibt? Ohne geeignete Schutzmaßnahmen können Webhooks zu einem Sicherheitsleck und einem Zuverlässigkeits-Albtraum werden.

In diesem Leitfaden gehen wir auf die Grundlagen der Absicherung von Webhooks mit signierten Payloads und der Implementierung robuster Retry-Logik ein. Sie lernen, wie Sie HMAC-Signaturen generieren und verifizieren, idempotente Handler entwerfen und einen Retry-Mechanismus aufbauen, der Fehler elegant behandelt. Am Ende verfügen Sie über einen produktionsreifen Ansatz für Webhooks.

Warum Webhooks Sicherheit und Zuverlässigkeit benötigen

Webhooks sind im Wesentlichen Server-zu-Server-HTTP-Anfragen, die durch Ereignisse ausgelöst werden. Anders als API-Aufrufe, die von einem Client initiiert werden, werden Webhooks von Ihrem Server an den Endpunkt eines Abonnenten gepusht. Dieses Push-Modell bringt zwei Hauptherausforderungen mit sich:

Das Ignorieren dieser Herausforderungen kann zu Datenlecks, verlorenen Ereignissen oder inkonsistenten Zuständen zwischen Systemen führen. Lassen Sie uns sie einzeln angehen.

Webhooks mit signierten Payloads absichern

Eine signierte Payload verwendet einen kryptografischen Hash, um die Integrität und Authentizität der Anfrage zu beweisen. Der gebräuchlichste Ansatz ist HMAC (Hash-based Message Authentication Code) mit einem gemeinsamen Geheimnis. So funktioniert es:

  1. Gemeinsames Geheimnis: Sowohl der Sender (Ihr Dienst) als auch der Empfänger (Abonnent) teilen einen geheimen Schlüssel. Dieser Schlüssel wird generiert, wenn der Abonnent seinen Webhook-Endpunkt registriert.
  2. Signaturgenerierung: Für jede Webhook-Anfrage berechnet der Sender einen HMAC des Anfrage-Bodys (und manchmal von Headern wie Zeitstempel) unter Verwendung des Geheimnisses. Die Signatur wird in einem Header gesendet, z. B. X-Signature.
  3. Signaturverifikation: Der Empfänger berechnet den HMAC mit demselben Geheimnis neu und vergleicht ihn mit der empfangenen Signatur. Wenn sie übereinstimmen, ist die Payload authentisch und unverändert.

Sehen wir uns ein Codebeispiel in Node.js zum Generieren und Verifizieren von Signaturen an.

const crypto = require('crypto');

// Sender: Signatur generieren
function generateSignature(payload, secret) {
  return crypto.createHmac('sha256', secret)
    .update(JSON.stringify(payload))
    .digest('hex');
}

// Empfänger: Signatur verifizieren
function verifySignature(payload, secret, receivedSignature) {
  const expectedSignature = generateSignature(payload, secret);
  // timingSafeEqual verwenden, um Timing-Angriffe zu verhindern
  return crypto.timingSafeEqual(
    Buffer.from(expectedSignature, 'hex'),
    Buffer.from(receivedSignature, 'hex')
  );
}

Wichtig: Verwenden Sie immer eine Vergleichsfunktion mit konstanter Zeit wie timingSafeEqual, um Timing-Angriffe zu vermeiden. Fügen Sie außerdem einen Zeitstempel in die signierten Daten ein, um Replay-Angriffe zu verhindern. Signieren Sie beispielsweise timestamp + '.' + payload und lehnen Sie Anfragen ab, die älter als ein paar Minuten sind.

Best Practices für signierte Payloads

Retry-Logik für zuverlässige Zustellung implementieren

Selbst mit Sicherheitsmaßnahmen können Webhooks aufgrund von Netzwerkproblemen, Serverfehlern oder Timeouts fehlschlagen. Ein robuster Retry-Mechanismus stellt sicher, dass Ereignisse letztendlich zugestellt werden. Hier ist ein schrittweiser Ansatz:

  1. Timeout festlegen: Definieren Sie ein angemessenes Timeout für Webhook-Anfragen (z. B. 10 Sekunden). Wenn der Abonnent nicht rechtzeitig antwortet, betrachten Sie es als Fehler.
  2. Antworten klassifizieren: Behandeln Sie 2xx-Statuscodes als Erfolg. Bei 4xx-Fehlern (außer 429) ist ein erneuter Versuch normalerweise sinnlos, da die Anfrage ungültig ist. Bei 5xx-Fehlern und Timeouts wiederholen Sie mit exponentiellem Backoff.
  3. Exponentielles Backoff mit Jitter: Planen Sie Wiederholungen in zunehmenden Intervallen (z. B. 1s, 5s, 30s, 5m, 30m) und fügen Sie zufälligen Jitter hinzu, um Thundering-Herd-Probleme zu vermeiden.
  4. Wiederholungen begrenzen: Legen Sie eine maximale Anzahl von Versuchen fest (z. B. 5) und markieren Sie die Zustellung dann als fehlgeschlagen. Benachrichtigen Sie den Abonnenten optional per E-Mail oder über ein Dashboard.
  5. Idempotenz: Da Wiederholungen zu doppelten Zustellungen führen können, fügen Sie eine eindeutige Ereignis-ID in die Payload und den Header ein. Abonnenten sollten diese ID verwenden, um die Verarbeitung zu deduplizieren.

Hier ist eine vereinfachte Retry-Queue-Implementierung in Python mit requests und time:

import requests
import time
import random

def deliver_webhook(url, payload, secret, max_retries=5):
    signature = generate_signature(payload, secret)
    headers = {'X-Signature': signature, 'Content-Type': 'application/json'}
    for attempt in range(max_retries):
        try:
            response = requests.post(url, json=payload, headers=headers, timeout=10)
            if 200 <= response.status_code < 300:
                return True
            elif response.status_code == 429 or 500 <= response.status_code < 600:
                # Wiederholbarer Fehler
                pass
            else:
                # Nicht wiederholbarer Fehler
                return False
        except requests.exceptions.RequestException:
            # Netzwerkfehler, wiederholen
            pass
        # Exponentielles Backoff mit Jitter
        delay = min(30, 2 ** attempt) + random.uniform(0, 1)
        time.sleep(delay)
    return False

Idempotenz: Umgang mit doppelten Zustellungen

Da Wiederholungen dazu führen können, dass dasselbe Ereignis mehrmals zugestellt wird, müssen Abonnenten eine idempotente Verarbeitung implementieren. Der Standardansatz besteht darin, eine eindeutige Ereignis-ID (z. B. eine UUID) in die Payload aufzunehmen und den Empfänger verarbeitete IDs für einen bestimmten Zeitraum speichern zu lassen. Wenn eine doppelte ID empfangen wird, bestätigt der Empfänger sie, ohne sie erneut zu verarbeiten.

Hier ist eine einfache Idempotenzprüfung in einem Webhook-Handler:

processed_events = set()

def handle_webhook(event):
    event_id = event['id']
    if event_id in processed_events:
        return 'already processed'
    # Ereignis verarbeiten
    processed_events.add(event_id)
    return 'success'

Verwenden Sie in der Produktion einen persistenten Speicher wie Redis oder eine Datenbank mit TTL, um verarbeitete IDs zu verfolgen.

Vergleich: Signierte Payloads vs. andere Sicherheitsmaßnahmen

Ansatz Vorteile Nachteile
HMAC-signierte Payloads Starke Integrität und Authentizität; keine Geheimnisse werden übertragen Erfordert Verwaltung gemeinsamer Geheimnisse
Basic Auth Einfach zu implementieren Anmeldeinformationen werden bei jeder Anfrage gesendet; weniger sicher
IP-Allowlisting Blockiert unbefugte Quellen IPs können sich ändern; nicht für dynamische Umgebungen geeignet
OAuth2 Client Credentials Standardisiert; tokenbasiert Übertrieben für einfache Webhooks; Overhead bei der Token-Verwaltung

HMAC-signierte Payloads bieten für die meisten Webhook-Anwendungsfälle eine gute Balance zwischen Sicherheit und Einfachheit.

Alles zusammenführen

Um ein zuverlässiges und sicheres Webhook-System aufzubauen:

Beim Debuggen von Webhook-Zustellungen ist die Inspektion der rohen HTTP-Anfragen und -Antworten von unschätzbarem Wert. Wenn Sie mit Nginx-Logs arbeiten, um eingehende Webhook-Anfragen zu verfolgen, kann der Nginx Log Analyzer Ihnen helfen, Log-Einträge zu parsen und zu filtern, um fehlgeschlagene Zustellungen oder verdächtige Muster zu identifizieren.

FAQ

Was ist eine signierte Webhook-Payload?

Eine signierte Webhook-Payload enthält eine kryptografische Signatur (normalerweise HMAC), die es dem Empfänger ermöglicht, die Authentizität und Integrität der Anfrage zu überprüfen. Der Sender berechnet die Signatur mit einem gemeinsamen Geheimnis und fügt sie in einen Header ein; der Empfänger berechnet sie neu und vergleicht sie.

Wie oft sollte ich einen fehlgeschlagenen Webhook wiederholen?

Eine gängige Praxis sind 3 bis 5 Wiederholungen mit exponentiellem Backoff. Danach markieren Sie die Zustellung als fehlgeschlagen und benachrichtigen den Abonnenten. Die genaue Anzahl hängt von Ihrem SLA und der Kritikalität des Ereignisses ab.

Warum ist Idempotenz für Webhooks wichtig?

Wiederholungen können zu doppelten Zustellungen führen. Idempotenz stellt sicher, dass die mehrfache Verarbeitung desselben Ereignisses dieselbe Wirkung hat wie die einmalige Verarbeitung, wodurch Datenkorruption oder doppelte Aktionen verhindert werden.