Webhooks erklärt: Signierte Payloads und Retry-Strategien
Sie haben eine API erstellt, die Webhooks an die Endpunkte Ihrer Nutzer sendet. Aber dann stehen Sie vor den klassischen Problemen: Wie stellen Sie sicher, dass der Payload nicht manipuliert wurde? Was passiert, wenn der Endpunkt nicht erreichbar ist? Wie verhindern Sie doppelte Verarbeitung? In diesem Artikel gehen wir diese Herausforderungen direkt an – mit signierten Payloads und robusten Retry-Strategien.
Warum Webhooks Signaturen brauchen
Webhooks sind HTTP-Callbacks, die andere Systeme über Ereignisse benachrichtigen. Da sie über das öffentliche Internet gesendet werden, sind sie anfällig für Abfangen und Spoofing. Ohne Signatur könnte ein Angreifer gefälschte Webhooks an Ihre Nutzer senden und unbeabsichtigte Aktionen auslösen. Das Signieren von Payloads mit einem gemeinsamen Secret gewährleistet Authentizität und Integrität.
Wie signierte Payloads funktionieren
Der gebräuchlichste Ansatz ist HMAC (Hash-based Message Authentication Code) mit SHA-256. Sender und Empfänger teilen einen geheimen Schlüssel. Der Sender berechnet eine Signatur über den Payload (und manchmal einen Zeitstempel) und fügt sie in einen Header ein. Der Empfänger berechnet die Signatur neu und vergleicht sie mit der empfangenen.
Schritt für Schritt: Einen Payload signieren
- Den Payload erstellen: Serialisieren Sie Ihre Ereignisdaten in JSON. Achten Sie auf konsistente Formatierung (z. B. keine zusätzlichen Leerzeichen), da die Signatur über die exakten Bytes berechnet wird.
- Einen Zeitstempel hinzufügen: Fügen Sie einen Unix-Zeitstempel in den Payload oder Header ein, um Replay-Angriffe zu verhindern. Der Empfänger kann Anfragen ablehnen, die älter als einige Minuten sind.
- Den HMAC berechnen: Verwenden Sie Ihren geheimen Schlüssel, um den HMAC-SHA256 des Payload-Strings (oder Zeitstempel + Payload) zu erzeugen.
- Die Signatur anhängen: Senden Sie sie in einem Header wie
X-Signature.
Hier ist ein Beispiel in Python:
import hmac
import hashlib
import json
import time
secret = b'your_shared_secret'
payload = {"event": "user.created", "data": {"id": 123}}
body = json.dumps(payload, separators=(',', ':'))
timestamp = str(int(time.time()))
signed_content = timestamp + '.' + body
signature = hmac.new(secret, signed_content.encode(), hashlib.sha256).hexdigest()
# Send headers: X-Signature: signature, X-Timestamp: timestamp
Die Signatur verifizieren
Auf der Empfängerseite müssen Sie:
- Den Zeitstempel und die Signatur aus den Headern extrahieren.
- Ablehnen, wenn der Zeitstempel zu alt ist (z. B. >5 Minuten), um Replay-Angriffe zu verhindern.
- Die Signatur mit derselben Methode neu berechnen.
- Mit einer Funktion mit konstanter Laufzeit vergleichen, um Timing-Angriffe zu vermeiden.
Beispielverifikation in Node.js:
const crypto = require('crypto');
function verifySignature(req, secret) {
const signature = req.headers['x-signature'];
const timestamp = req.headers['x-timestamp'];
if (!signature || !timestamp) return false;
if (Date.now() / 1000 - timestamp > 300) return false; // 5 min
const body = JSON.stringify(req.body);
const signed = timestamp + '.' + body;
const expected = crypto.createHmac('sha256', secret).update(signed).digest('hex');
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}
Retry-Strategien entwerfen
Auch mit Signaturen können Webhooks aufgrund von Netzwerkproblemen, Serverfehlern oder Timeouts fehlschlagen. Eine robuste Retry-Strategie stellt die Zustellung sicher, ohne den Empfänger zu überlasten.
Wichtige Überlegungen
- Idempotenz: Empfänger sollten doppelte Ereignisse problemlos verarbeiten können. Verwenden Sie einen Idempotenzschlüssel (z. B. Ereignis-ID) zur Deduplizierung.
- Exponentielles Backoff: Wiederholen Sie nach zunehmenden Verzögerungen (z. B. 1s, 5s, 30s, 5m, 30m), um dem Empfänger Zeit zur Erholung zu geben.
- Maximale Wiederholungen: Legen Sie ein Limit fest (z. B. 5 Versuche) und markieren Sie den Webhook dann als fehlgeschlagen, möglicherweise mit Benachrichtigung des Nutzers.
- Timeouts: Legen Sie für jeden Versuch ein angemessenes Timeout fest (z. B. 10 Sekunden).
Retries implementieren
Die meisten Webhook-Sender verwenden ein Warteschlangensystem (z. B. Redis, RabbitMQ oder eine Datenbank), um Wiederholungen zu planen. Hier ist ein vereinfachter Ablauf:
- Bei Auslösung des Ereignisses wird ein Webhook-Job mit dem Payload und der Ziel-URL in die Warteschlange gestellt.
- Ein Worker nimmt den Job auf und versucht die Zustellung.
- Wenn der Antwortstatus 2xx ist, als erfolgreich markieren.
- Wenn nicht, eine Wiederholung mit exponentiellem Backoff planen, bis zur maximalen Anzahl von Versuchen.
- Nach der maximalen Anzahl von Versuchen als fehlgeschlagen markieren und benachrichtigen.
Beispiel für einen Wiederholungsplan:
| Versuch | Verzögerung |
|---|---|
| 1 | Sofort |
| 2 | 1 Minute |
| 3 | 5 Minuten |
| 4 | 30 Minuten |
| 5 | 2 Stunden |
Best Practices für Webhook-Sicherheit
- HTTPS verwenden: Stellen Sie Webhooks immer über TLS zu, um Abhören zu verhindern.
- Secrets rotieren: Rotieren Sie gemeinsame Secrets regelmäßig und unterstützen Sie während der Rotation mehrere aktive Secrets.
- URLs validieren: Stellen Sie sicher, dass die Webhook-URL HTTPS verwendet und nicht auf interne IPs zeigt (um SSRF zu verhindern).
- Protokollieren und überwachen: Führen Sie Protokolle über Zustellversuche und -fehler für Debugging und Auditing.
FAQ
Was ist ein signierter Webhook-Payload?
Ein signierter Payload enthält eine kryptografische Signatur (normalerweise HMAC), die es dem Empfänger ermöglicht, die Authentizität des Senders und die Unversehrtheit des Payloads zu überprüfen.
Wie oft sollte ich einen fehlgeschlagenen Webhook wiederholen?
Es gibt keine pauschale Antwort, aber ein üblicher Ansatz sind 3–5 Wiederholungen mit exponentiellem Backoff. Berücksichtigen Sie die Kritikalität des Ereignisses und die Kapazität des Empfängers.
Kann ich JWT für Webhook-Signaturen verwenden?
Ja, JWT kann verwendet werden, ist aber komplexer. HMAC ist einfacher und für die meisten Anwendungsfälle ausreichend. JWT ist nützlich, wenn Sie Claims oder Ablaufzeiten in die Signatur selbst aufnehmen möchten.
Bereit, Ihre Webhook-Payloads zu testen? Verwenden Sie unseren JSON Formatter, um Ihre JSON-Payloads vor dem Senden zu validieren und übersichtlich zu formatieren.