Webhooks erklärt: Signierte Payloads und Retries
Sie haben gerade einen Zahlungsanbieter eines Drittanbieters integriert. In der Dokumentation steht: „Wir senden einen Webhook, wenn eine Zahlung erfolgreich ist.“ Einfach, oder? Aber dann stellen Sie fest: Woher wissen Sie, dass die Anfrage wirklich von ihnen kommt? Was ist, wenn Ihr Server nicht erreichbar ist, wenn sie ihn senden? Was ist, wenn sie dasselbe Ereignis zweimal senden?
Webhooks sind leistungsstark, erfordern aber eine sorgfältige Handhabung. Dieser Leitfaden behandelt die Grundlagen: das Signieren von Payloads zur Überprüfung der Authentizität, die Implementierung von Wiederholungsversuchen zur Behandlung von Fehlern und die Gewährleistung von Idempotenz zur Vermeidung doppelter Verarbeitung.
Was sind Webhooks?
Ein Webhook ist ein HTTP-Callback: Anstatt dass Ihre App eine API auf Änderungen abfragt, sendet der Anbieter bei Auftreten eines Ereignisses eine HTTP-POST-Anfrage an eine von Ihnen angegebene URL. Dies macht Integrationen effizient und nahezu in Echtzeit.
Häufige Beispiele sind Zahlungsbenachrichtigungen (Stripe, PayPal), CI/CD-Trigger (GitHub, GitLab) und Messaging-Plattformen (Slack, Discord).
Warum Webhooks mit Signaturen absichern?
Ihr Webhook-Endpunkt ist öffentlich zugänglich. Jeder kann eine POST-Anfrage an ihn senden. Ohne Überprüfung könnte ein Angreifer Ereignisse fälschen – beispielsweise eine unbezahlte Bestellung als bezahlt markieren.
Das Signieren von Payloads mit einem gemeinsamen Geheimnis ermöglicht es Ihnen zu überprüfen, ob die Anfrage vom erwarteten Absender stammt und ob der Payload nicht manipuliert wurde.
Wie HMAC-Signaturen funktionieren
Der Anbieter und Sie teilen einen geheimen Schlüssel. Für jeden Webhook berechnet der Anbieter einen HMAC (Hash-based Message Authentication Code) des Payloads mit diesem Geheimnis und fügt die Signatur in einen Header ein (z. B. X-Signature). Sie berechnen den HMAC auf Ihrer Seite neu und vergleichen.
HMAC gewährleistet Integrität und Authentizität, da nur jemand mit dem Geheimnis eine gültige Signatur erzeugen kann.
Implementierung signierter Payloads
Lassen Sie uns eine typische Implementierung in Node.js (Express) und Python (Flask) durchgehen. Die Prinzipien gelten für jede Sprache.
Schritt 1: Geheimnis erhalten und speichern
Bei der Registrierung des Webhooks gibt Ihnen der Anbieter ein Geheimnis. Speichern Sie es sicher – in Umgebungsvariablen oder einem Secrets Manager – niemals im Code.
Schritt 2: Den Rohtext erfassen
Die Signaturüberprüfung erfordert die exakten Rohbytes des Bodys. Wenn Ihr Framework JSON analysiert, bevor Sie überprüfen, kann eine erneute Serialisierung die Bytes verändern und die Überprüfung fehlschlagen lassen.
Verwenden Sie in Express express.raw({ type: 'application/json' }) für die Webhook-Route. Greifen Sie in Flask auf request.get_data() vor der Analyse zu.
Schritt 3: Signaturen berechnen und vergleichen
Verwenden Sie eine Vergleichsfunktion mit konstanter Zeit, um Timing-Angriffe zu verhindern.
// Node.js (Express)
const crypto = require('crypto');
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-signature'];
const secret = process.env.WEBHOOK_SECRET;
const expected = crypto.createHmac('sha256', secret).update(req.body).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
return res.status(401).send('Invalid signature');
}
// Process event
const event = JSON.parse(req.body);
// ... handle event
res.status(200).send('OK');
});
Verwenden Sie in Python hmac.compare_digest:
# Python (Flask)
import hmac, hashlib, os
from flask import request, abort
@app.route('/webhook', methods=['POST'])
def webhook():
signature = request.headers.get('X-Signature')
secret = os.environ['WEBHOOK_SECRET'].encode()
expected = hmac.new(secret, request.get_data(), hashlib.sha256).hexdigest()
if not hmac.compare_digest(signature, expected):
abort(401)
event = request.get_json()
# ... handle event
return 'OK', 200
Schritt 4: Zeitstempel behandeln (optional, aber empfohlen)
Einige Anbieter fügen einen Zeitstempel in die Signatur ein, um Replay-Angriffe zu verhindern. Beispielsweise signiert das Schema von Stripe timestamp.payload. Sie sollten Anfragen mit Zeitstempeln ablehnen, die zu weit in der Vergangenheit liegen (z. B. älter als 5 Minuten).
Implementierung von Wiederholungsversuchen
Netzwerke versagen. Ihr Server könnte ausgefallen sein oder die Anfrage könnte eine Zeitüberschreitung haben. Zuverlässige Webhook-Systeme wiederholen fehlgeschlagene Zustellungen.
Auf der Anbieterseite
Wenn Sie den Webhook-Sender erstellen, implementieren Sie Wiederholungsversuche mit exponentiellem Backoff. Ein übliches Muster: Wiederholung nach 1 Minute, dann 5 Minuten, dann 30 Minuten usw., bis zu einer maximalen Anzahl von Versuchen (z. B. 5). Fügen Sie eine eindeutige Ereignis-ID in den Payload ein, damit der Empfänger deduplizieren kann.
Auf der Empfängerseite
Als Empfänger sollten Sie:
- Schnell antworten (innerhalb weniger Sekunden) mit einem 2xx-Status, um den Empfang zu bestätigen. Führen Sie umfangreiche Verarbeitung asynchron durch.
- Für Fehler, die eine Wiederholung rechtfertigen (z. B. 500), einen Nicht-2xx-Status zurückgeben. Aber seien Sie vorsichtig: Wenn Sie 4xx zurückgeben, wiederholt der Anbieter möglicherweise nicht.
- Ihren Endpunkt idempotent machen, damit doppelte Zustellungen keine Probleme verursachen.
Idempotenz: Umgang mit doppelten Ereignissen
Wiederholungsversuche können zu doppelten Ereignissen führen. Verwenden Sie die Ereignis-ID (von den meisten Anbietern bereitgestellt), um verarbeitete Ereignisse zu verfolgen. Prüfen Sie vor der Verarbeitung, ob die ID bereits gesehen wurde; wenn ja, überspringen Sie sie.
Speichern Sie verarbeitete IDs in einer Datenbank mit einer TTL oder verwenden Sie einen Idempotenzschlüssel in Ihrer Geschäftslogik.
Webhooks lokal testen
Während der Entwicklung ist Ihr lokaler Server nicht öffentlich zugänglich. Tools wie ngrok oder localtunnel erstellen eine öffentliche URL, die zu Ihrem Rechner tunnelt. Alternativ können Sie einen Webhook-Testdienst verwenden, der Anfragen erfasst und Ihnen das erneute Abspielen ermöglicht.
Häufige Fallstricke und Best Practices
- Vertrauen Sie dem Payload nicht vor der Überprüfung. Überprüfen Sie immer zuerst die Signatur.
- Verwenden Sie HTTPS. Webhooks übertragen sensible Daten; verschlüsseln Sie während der Übertragung.
- Protokollieren Sie alles. Protokollieren Sie die Rohdaten der Anfrage (Header und Body) zum Debuggen, aber schwärzen Sie sensible Informationen.
- Überwachen Sie Fehler. Richten Sie Warnungen für wiederholte Webhook-Fehler ein.
- Versionieren Sie Ihre Ereignisse. Fügen Sie eine Version in den Payload ein, damit Sie sich weiterentwickeln können, ohne Empfänger zu brechen.
Vergleich: Signierte vs. unsignierte Webhooks
| Aspekt | Signiert | Unsigniert |
|---|---|---|
| Sicherheit | Hoch: überprüft Herkunft und Integrität | Niedrig: jeder kann fälschen |
| Implementierungsaufwand | Mittel: HMAC berechnen und vergleichen | Minimal |
| Empfohlen für | Alle Produktionssysteme | Nur interne, vertrauenswürdige Netzwerke |
FAQ
Wie überprüfe ich eine Webhook-Signatur?
Berechnen Sie einen HMAC des Rohtexts der Anfrage mit dem gemeinsamen Geheimnis und vergleichen Sie ihn dann mit dem Signatur-Header mithilfe einer Funktion mit konstanter Zeit. Verwenden Sie immer den Rohtext, nicht analysiertes JSON.
Was sollte mein Webhook-Endpunkt zurückgeben?
Geben Sie schnell einen 2xx-Statuscode zurück, um den Empfang zu bestätigen. Wenn die Verarbeitung fehlschlägt, geben Sie einen 5xx zurück, um eine Wiederholung auszulösen, aber stellen Sie sicher, dass Ihr Endpunkt idempotent ist, um Duplikate zu verarbeiten.
Wie oft wird ein Webhook wiederholt?
Das hängt vom Anbieter ab. Die meisten wiederholen mehrmals mit exponentiellem Backoff über einen Zeitraum von Stunden oder Tagen. Überprüfen Sie die Dokumentation des Anbieters für Details.
Beim Debuggen von Webhook-Zustellungen ist die Inspektion von Serverprotokollen oft notwendig. Wenn Sie Nginx verwenden, kann der Nginx Log Analyzer Ihnen helfen, Zugriffsprotokolle zu analysieren und fehlgeschlagene Anfragen oder ungewöhnliche Muster zu erkennen.