Webhooks erklärt: Signierte Payloads und Retries
Sie haben gerade den Webhook eines Zahlungsanbieters integriert. Die Dokumentation besagt: „Wir senden eine POST-Anfrage an Ihren Endpunkt, wenn eine Zahlung erfolgreich ist.“ Einfach, oder? Dann bemerken Sie doppelte Ereignisse, verpasste Benachrichtigungen und eine Sicherheitswarnung über gefälschte Anfragen. Webhooks sind im Konzept einfach, aber in der Produktion knifflig. Dieser Artikel erklärt, wie Sie Webhooks korrekt implementieren, indem Sie signierte Payloads und Retries verwenden, damit Sie zuverlässige und sichere Integrationen erstellen können.
Was sind Webhooks?
Ein Webhook ist eine umgekehrte API: Statt dass Ihre Anwendung einen Dienst nach Updates abfragt, sendet der Dienst Ereignisse per HTTP POST an Ihren Endpunkt. Das ist effizient und nahezu in Echtzeit. Häufige Anwendungsfälle sind Zahlungsbenachrichtigungen, CI/CD-Trigger und Chat-Integrationen.
Aber Webhooks bringen Herausforderungen mit sich: Woher wissen Sie, dass die Anfrage wirklich vom Anbieter stammt? Was, wenn Ihr Server ausgefallen ist? Was, wenn dasselbe Ereignis zweimal ankommt? Lassen Sie uns diese nacheinander angehen.
Webhooks mit signierten Payloads absichern
Jeder kann eine HTTP-Anfrage an Ihren Endpunkt senden. Ohne Verifikation könnten Angreifer Ereignisse fälschen, was zu Betrug oder Datenverfälschung führen kann. Die Standardlösung besteht darin, den Payload mit einem gemeinsamen Geheimnis zu signieren.
Wie HMAC-Signaturen funktionieren
Der Anbieter und Sie teilen einen geheimen Schlüssel. Für jedes Ereignis berechnet der Anbieter einen HMAC (Hash-based Message Authentication Code) des Request-Bodys mit diesem Geheimnis und sendet die Signatur in einem Header (z. B. X-Signature). Sie berechnen den HMAC auf Ihrer Seite neu und vergleichen. Wenn sie übereinstimmen, ist die Anfrage authentisch und unverändert.
Hier ist ein minimales Beispiel in Python mit Flask:
import hmac
import hashlib
from flask import Flask, request, abort
app = Flask(__name__)
SECRET = b'your_shared_secret'
@app.route('/webhook', methods=['POST'])
def webhook():
signature = request.headers.get('X-Signature')
if not signature:
abort(400, 'Missing signature')
body = request.get_data()
expected = hmac.new(SECRET, body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, signature):
abort(401, 'Invalid signature')
# Process the event
return '', 200
Verwenden Sie immer hmac.compare_digest für einen Vergleich mit konstanter Zeit, um Timing-Angriffe zu verhindern. Verifizieren Sie außerdem die Signatur, bevor Sie den JSON-Body parsen, um unnötige Verarbeitung zu vermeiden.
Best Practices für Signaturen
- Verwenden Sie ein starkes Geheimnis: Generieren Sie einen zufälligen Schlüssel von mindestens 32 Bytes und speichern Sie ihn sicher (z. B. als Umgebungsvariable).
- Fügen Sie einen Zeitstempel hinzu: Um Replay-Angriffe zu verhindern, fügen Anbieter oft einen Zeitstempel in den signierten Payload ein. Lehnen Sie Anfragen ab, die älter als ein paar Minuten sind.
- Rotieren Sie Geheimnisse regelmäßig: Unterstützen Sie während der Rotation mehrere aktive Geheimnisse, um Ausfallzeiten zu vermeiden.
- Dokumentieren Sie den Signaturalgorithmus: Geben Sie die Hash-Funktion (SHA-256 ist Standard) und den Header-Namen an.
Fehler mit Retries behandeln
Webhooks sind aus Sicht des Anbieters Fire-and-Forget, aber Ihr Endpunkt könnte vorübergehend nicht verfügbar sein. Um die Zustellung sicherzustellen, implementieren Anbieter Retry-Richtlinien. Als Empfänger müssen Sie auf Retries und Duplikate vorbereitet sein.
Von Anbietern verwendete Retry-Strategien
Die meisten Anbieter wiederholen mit exponentiellem Backoff: z. B. sofort, dann nach 1 Minute, 5 Minuten, 30 Minuten usw., bis zu einer maximalen Anzahl von Versuchen (oft 5–10). Einige ermöglichen es Ihnen auch, einen Retry-Zeitplan zu konfigurieren. Das genaue Verhalten variiert, lesen Sie daher die Dokumentation des Anbieters.
Ihren Endpunkt für Retries entwerfen
Ihr Endpunkt sollte:
- Schnell antworten: Bestätigen Sie den Empfang mit einem 2xx-Status innerhalb weniger Sekunden. Führen Sie aufwändige Verarbeitung asynchron durch (z. B. das Ereignis in eine Warteschlange stellen).
- Idempotent sein: Dasselbe Ereignis kann mehrmals zugestellt werden. Verwenden Sie einen Idempotenzschlüssel (oft im Ereignis-Payload oder Header bereitgestellt), um zu deduplizieren. Speichern Sie verarbeitete Ereignis-IDs für einen angemessenen Zeitraum.
- Geeignete Statuscodes zurückgeben: 2xx für Erfolg, 4xx für Client-Fehler (Anbieter wiederholt möglicherweise nicht), 5xx für Server-Fehler (Anbieter wird wiederholen).
Beispiel für eine Idempotenzprüfung in Python:
processed_events = set() # In production, use a database or cache
@app.route('/webhook', methods=['POST'])
def webhook():
# ... signature verification ...
event = request.json
event_id = event['id']
if event_id in processed_events:
return '', 200 # Already processed
# Process event
processed_events.add(event_id)
return '', 200
Testen Ihrer Webhook-Implementierung
Bevor Sie live gehen, testen Sie Ihren Endpunkt gründlich:
- Simulieren Sie signierte Anfragen: Schreiben Sie Unit-Tests, die gültige und ungültige Signaturen generieren.
- Testen Sie Retry-Szenarien: Simulieren Sie Fehler und stellen Sie sicher, dass Ihr Endpunkt Duplikate behandelt.
- Verwenden Sie Anbieter-Tools: Viele Anbieter bieten eine CLI oder ein Dashboard zum Senden von Testereignissen.
- Überwachen und protokollieren Sie: Protokollieren Sie eingehende Ereignisse (ohne sensible Daten) und richten Sie Warnungen für Signaturfehler oder hohe Fehlerraten ein.
Vergleich: Polling vs. Webhooks
| Aspekt | Polling | Webhooks |
|---|---|---|
| Latenz | Abhängig vom Intervall (z. B. Minuten) | Nahezu Echtzeit |
| Serverlast | Hoch (häufige Anfragen) | Niedrig (nur bei Ereignissen) |
| Komplexität | Einfach zu implementieren | Erfordert Signaturverifikation und Retry-Behandlung |
| Zuverlässigkeit | Sie kontrollieren Retries | Anbieter kontrolliert Retries; Sie müssen Duplikate behandeln |
FAQ
Wie verifiziere ich eine Webhook-Signatur?
Berechnen Sie den HMAC des rohen Request-Bodys mit dem gemeinsamen Geheimnis und vergleichen Sie ihn mit dem Signatur-Header unter Verwendung einer Vergleichsfunktion mit konstanter Zeit. Verifizieren Sie immer, bevor Sie den Payload verarbeiten.
Was sollte mein Endpunkt zurückgeben, um einen Webhook zu bestätigen?
Geben Sie einen 2xx-Statuscode (z. B. 200 OK) zurück, sobald Sie das Ereignis empfangen und zur Verarbeitung in die Warteschlange gestellt haben. Vermeiden Sie lange Verarbeitung im Request-Handler, um Timeouts zu verhindern.
Wie kann ich doppelte Webhook-Zustellungen behandeln?
Verwenden Sie einen Idempotenzschlüssel aus dem Ereignis-Payload (oder eine eindeutige Ereignis-ID), um verarbeitete Ereignisse zu verfolgen. Speichern Sie diese IDs in einer Datenbank oder einem Cache und überspringen Sie die Verarbeitung, wenn die ID zuvor gesehen wurde.
Bereit, Ihre Webhook-Payloads zu testen? Verwenden Sie unseren JSON Formatter, um den JSON-Body eingehender Webhooks übersichtlich zu formatieren und zu validieren, was das Debuggen erleichtert.