Webhooks erklärt: Signierte Payloads und Retries
Sie haben ein Zahlungsgateway oder einen CI/CD-Dienst integriert und müssen nun Echtzeit-Events empfangen. Webhooks sind die Standardlösung, bringen aber zwei große Herausforderungen mit sich: die Überprüfung, dass die Anfrage tatsächlich vom erwarteten Absender stammt, und der Umgang mit Fehlern, wenn Ihr Endpunkt nicht erreichbar oder langsam ist. Dieser Artikel erklärt, wie Sie signierte Webhook-Payloads und robuste Retry-Logik mit praktischen Codebeispielen implementieren.
Was sind Webhooks?
Ein Webhook ist ein benutzerdefinierter HTTP-Callback. Anstatt dass Ihre Anwendung eine API nach Updates abfragt, sendet der Anbieter bei jedem Ereignis eine HTTP-POST-Anfrage an eine von Ihnen angegebene URL. Dies macht Integrationen effizient und nahezu in Echtzeit.
Webhooks sind jedoch push-basiert, was bedeutet, dass Ihr Endpunkt öffentlich zugänglich sein muss, unerwartete Last bewältigen und mit doppelten oder böswilligen Anfragen umgehen muss. Hier kommen Signierung und Retries ins Spiel.
Warum sollten Webhook-Payloads signiert werden?
Ohne Verifikation könnte jeder, der Ihre Webhook-URL kennt, gefälschte Events senden. Durch die Signierung von Payloads mit einem gemeinsamen Secret können Sie den Absender authentifizieren und sicherstellen, dass der Payload nicht manipuliert wurde.
Der gebräuchlichste Ansatz ist HMAC (Hash-based Message Authentication Code) mit SHA-256. Der Anbieter berechnet eine Signatur über den rohen Request-Body mit einem geheimen Schlüssel und fügt sie in einen Header ein (z. B. X-Signature). Ihr Server berechnet die Signatur neu und vergleicht sie.
Implementierung der HMAC-Verifikation
Hier ist ein einfaches Beispiel in Node.js mit Express:
const crypto = require('crypto');
const express = require('express');
const app = express();
// Use raw body for signature verification
app.use(express.raw({ type: 'application/json' }));
app.post('/webhook', (req, res) => {
const signature = req.headers['x-signature'];
const secret = process.env.WEBHOOK_SECRET;
const payload = req.body; // Buffer
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
return res.status(401).send('Invalid signature');
}
// Process the event
const event = JSON.parse(payload);
console.log('Received event:', event.type);
res.status(200).send('OK');
});
app.listen(3000, () => console.log('Webhook server running'));
Wichtige Punkte:
- Rohen Body verwenden: Parsen Sie den Body als rohe Bytes, nicht als JSON, da jede Transformation die Signatur verändert.
- Timing-sicherer Vergleich: Verwenden Sie
crypto.timingSafeEqual, um Timing-Angriffe zu verhindern. - Secret-Verwaltung: Speichern Sie Secrets sicher (Umgebungsvariablen, Secret Manager).
Umgang mit Retries: Webhooks zuverlässig machen
Netzwerke fallen aus, Server stürzen ab und Deployments verursachen Ausfallzeiten. Ein zuverlässiges Webhook-System muss fehlgeschlagene Zustellungen wiederholen. Aber naive Retries können zu doppelter Verarbeitung führen und Ihr System überlasten.
Retry-Strategien
Gängige Retry-Muster sind:
- Exponentielles Backoff: Längere Wartezeiten zwischen jedem Retry (z. B. 1s, 2s, 4s, 8s...).
- Maximales Retry-Limit: Nach N Versuchen aufhören (z. B. 5) und die Zustellung als fehlgeschlagen markieren.
- Jitter: Zufälligkeit hinzufügen, um Thundering-Herd-Probleme zu vermeiden.
Die meisten Anbieter implementieren etwas wie: bis zu 5 Wiederholungen mit exponentiellem Backoff über 24 Stunden.
Idempotenz: Der Schlüssel zu sicheren Retries
Da Retries dasselbe Event mehrmals zustellen können, muss Ihr Endpunkt idempotent sein. Fügen Sie eine eindeutige Event-ID in den Payload ein und speichern Sie verarbeitete IDs, um Duplikate zu überspringen.
Beispiel mit einem einfachen In-Memory-Set (verwenden Sie in der Produktion eine Datenbank):
const processedEvents = new Set();
app.post('/webhook', (req, res) => {
// ... signature verification ...
const event = JSON.parse(payload);
if (processedEvents.has(event.id)) {
return res.status(200).send('Already processed');
}
// Process event
processedEvents.add(event.id);
res.status(200).send('OK');
});
Verwenden Sie für die Produktion eine Datenbank mit einer Unique-Constraint auf der Event-ID.
Best Practices für Webhook-Konsumenten
- Schnell antworten: Bestätigen Sie den Empfang mit einem 2xx-Status, bevor Sie mit der aufwändigen Verarbeitung beginnen. Stellen Sie das Event für die asynchrone Verarbeitung in eine Warteschlange.
- Zeitstempel validieren: Wenn der Anbieter einen Zeitstempel mitschickt, lehnen Sie Anfragen ab, die älter als ein paar Minuten sind, um Replay-Angriffe zu verhindern.
- HTTPS verwenden: Stellen Sie Ihren Webhook-Endpunkt immer über HTTPS bereit, um Payloads während der Übertragung zu schützen.
- Alles loggen: Protokollieren Sie eingehende Anfragen, Signaturen und Verarbeitungsergebnisse zum Debuggen.
- Fehler überwachen: Richten Sie Alarme für wiederholte Zustellungsfehler ein.
Vergleich von Retry-Richtlinien
| Richtlinie | Vorteile | Nachteile |
|---|---|---|
| Festes Intervall | Einfach zu implementieren | Kann ein sich erholendes System überlasten |
| Exponentielles Backoff | Reduziert die Last während Ausfällen | Verzögert die Zustellung bei vorübergehenden Fehlern |
| Exponentielles Backoff mit Jitter | Verhindert synchronisierte Retries | Etwas komplexer |
FAQ
Wie verifiziere ich eine Webhook-Signatur?
Berechnen Sie den HMAC des rohen Request-Bodys mit dem gemeinsamen Secret und vergleichen Sie ihn mit dem Signatur-Header unter Verwendung einer timing-sicheren Funktion. Vergleichen Sie niemals mit einfacher Gleichheit.
Was soll ich tun, wenn mein Webhook-Endpunkt nicht erreichbar ist?
Der Anbieter sollte mit exponentiellem Backoff wiederholen. Stellen Sie sicher, dass Ihr Endpunkt schnell einen 2xx-Status zurückgibt und Events asynchron verarbeitet, um die Auswirkungen von Ausfallzeiten zu minimieren.
Kann ich Webhooks ohne Signierung verwenden?
Es wird nicht empfohlen. Ohne Signaturen können Sie den Absender nicht verifizieren, was Ihren Endpunkt anfällig für gefälschte Events macht. Verwenden Sie immer Signaturen, wenn verfügbar.
Beim Testen von Webhook-Payloads müssen Sie möglicherweise JSON-Daten inspizieren. Verwenden Sie den JSON Formatter, um Payloads schnell zu verschönern und zu validieren.