Sie haben ein Dienst, der reagieren muss, wenn etwas an einem anderen Ort passiert. Ein Zahlungsauftrag wird abgewickelt. Ein Kundenverzeichnis ändert sich. Ein Repository wird gepusht. Sie könnten ein API alle Minute abfragen und Zyklen vergeuden, indem Sie immer wieder 'Gibt es etwas Neues?' fragen, oder Sie lassen das Quellsystem Sie anrufen, wenn das Ereignis eintritt.
Dort enden die meisten Web Hook-Beispielartikel. Sie zeigen eine Route, drucken die JSON-Körperform, geben sie zurück 200, und dann ist es erledigt. Diese Version funktioniert bis jemand eine gefälschte Anfrage sendet, eine gültige wiederholt oder der Handler bricht, weil die Framework die Anfrage vor der Signaturprüfung verarbeitet hat.
Diese Anleitung nimmt den Weg, den Sie in der Produktion verwenden werden. Die Beispiele sind klein genug, um zu kopieren, aber sie enthalten die wichtigen Teile: Rohkörperverarbeitung, HMAC-Verifizierung, Zeitstempelprüfung, schnelle Bestätigung und praktische Debugging.
Inhaltsverzeichnis
- Was sind Webhooks und warum sollten Sie sie verwenden?
- Anatomie einer Webhook-HTTP-Anfrage
- Wie Sie Webhook-Signaturen sicher überprüfen
- Schutz vor Wiederholungsangriffen
- Erstellung eines Webhook-Empfängers in Node.js
- Erstellung eines Webhook-Empfängers in Python
- Ein Beispiel für einen Webhook-Empfänger in Go erstellen
- Wichtige Debugging-Techniken
- Ein Checkliste für Webhooks, die für die Produktion bereit sind
- Häufig gestellte Fragen zu Webhooks
Was sind Webhooks und warum sollten Sie sie verwenden?
Ihr Rechnungsanbieter markiert eine Rechnung als bezahlt um 02:13. Wenn Ihre App um 02:14 davon erfährt, erhält der Kunde sofort Zugriff. Wenn Ihre App davon erst auf dem nächsten Polling-Zyklus erfährt, müssen sie warten, Support erhält eine Ticketanfrage und Ihre Protokolle füllen sich mit unnötigem Lärm. Webhooks lösen dieses Zeitungsproblem, indem sie einen HTTP-Aufruf senden, wenn das Ereignis eintritt.
In der Praxis ist ein Webhook ein Ereignis-getriebener POST von einem System zu einem anderen. Der Anbieter erkennt eine Änderung, wie z.B. invoice.paid, order.createdoder pushund sendet die Ereignisdaten an eine URL, die Sie kontrollieren. Das entfernt den ständigen "Gibt es etwas Neues?"-Zyklus, den Polling erzeugt und reduziert die Anzahl der verschwendeten Anfragen.
Dieses Muster tritt in realen Systemen auf, weil es sauber auf Geschäftsevents abbildet. Stripe sendet Zahlungsabschlüsse. GitHub sendet Aktivitäten im Repository. Shopify sendet Aktualisierungen für Bestellungen. Die Form ist einfach, aber die Produktionsverhalten ist nicht. Ein Webhook, der Geld, Zugriff oder Bestände aktualisiert, verdient die gleiche Sorgfalt wie jeder öffentliche API-Endpunkt, insbesondere wenn sich Wiederholungen, Duplikate und unvertrauenswürdige Traffic im Bild haben.
Das mentale Modell, das hilft
Ein nützlicher Weg, um einen Webhook-Fluss zu umschreiben, ist als vier Teile, die zusammenarbeiten:
- Quellensystem. Die Dienst, die das Ereignis erkennt.
- Zielendpunkt. Ihre HTTP-Routen, die es empfängt.
- Ereignis. Der benannte Wechsel, der aufgetreten ist, wie
invoice.paidoderpush. - Payload. Der Anforderungskörper mit den Details, die Ihr code benötigt.
Der Anbieter sendet Fakten über etwas, das bereits passiert ist. Ihre Aufgabe ist es, den Absender zu überprüfen, die Anfrage als frisch zu bestätigen und die Änderung einmal anzuwenden. Diese letzte Stelle ist wichtiger als viele grundlegende Tutorials zugeben.
Praktische Regel: Verwenden Sie Webhooks für Ereignis-gesteuerte Updates. Verwenden Sie Polling für geplante Lesen, Nachrüstungen oder Anbieter, die keine ausgehenden Ereignisse anbieten.
Für Teams, die breitere Workflow-Automatisierung und Datenintegration, werden Webhooks normalerweise die Ereignisebene darstellen, die die Systeme ohne unnötigen Anfrageverkehr in Einklang bringt. Wenn Sie an Integrationsschwerpunkten arbeiten, sind Capgo’s Hintergrundentwicklungsartikel wichtige Kontext, weil Kernprobleme sich um Wiederholungen, Warteschlangen, Beobachtbarkeit und Fehlerbehandlung drehen.
Was funktioniert und was scheitert in der Produktion
Die Konfigurationen, die gut funktionieren, sind normalerweise langweilig durch Design. Abonnieren Sie nur die Ereignisse, die Sie benötigen. Halten Sie Endpunkte durch Anbieter oder Ereignisfamilie abgegrenzt. Speichern Sie Ereignis-IDs, damit Duplikate nicht wiederholte Nebeneffekte auslösen. Gehen Sie mit einer schnellen 2xx-Antwort aus, sobald die Anfrage validiert und in die Warteschlange gelegt wurde, dann führen Sie die Geschäftslogik asynchron langsamer aus.
Die verletzliche Version ist leicht zu erkennen. Ein generischer Endpunkt handhabt alles. Die Signaturprüfungen werden während der frühen Testphase ausgelassen und kommen nie wieder zurück. Der Handler schreibt direkt in kritische Tabellen, bevor er überprüft, ob der Ereignis echt oder veraltet ist. Das funktioniert in einer Demo und scheitert bei Wiederholungsstürmen, Provider-Ausfällen oder einem Angreifer, der alte Anforderungen wiederholt.
Dieser Kompromiss definiert den Rest dieser Anleitung. Die 'Hallo-Welt'-Version eines Webhook-Empfängers ist klein. Die Produktionsreife- Version fügt die Signaturprüfung, die Wiederholungsabwehr, die Duplikatbearbeitung und die Debugging-Hooks von Anfang an hinzu.
Anatomie eines Webhook-HTTP-Anforderung
Bevor man code schreibt, hilft es, den Anforderung als Roh-HTTP anstatt als Framework-Objekt anzusehen. Ein typischer Webhook ist einfach ein HTTP-POST an einen öffentlichen Endpunkt mit Kopfzeilen und einem JSON-Körper.
Einfache Rohanforderung
POST /webhooks/orders HTTP/1.1
Host: your-app.example
Content-Type: application/json
User-Agent: Provider-Webhooks/1.0
X-Webhook-Signature: sha256=abc123example
X-Webhook-Timestamp: 1712345678
{
"event": "order.created",
"id": "evt_123",
"data": {
"order_id": "ord_456",
"status": "created"
}
}
Die wichtigen Teile sind einfach zu verstehen:
- MethodeBei der Praxis sind Webhook-Lieferungen normalerweise POST-Anforderungen.
- InhaltstypDie meisten modernen Anbieter senden JSON.
- Benutzer-AgentHilfreich für die Debugging, aber nie genug für Vertrauen.
- SignaturkopfzeileTrägt die Authentifizierungsprüfung des Providers.
- Timestamp-KopfzeileWird verwendet, um veraltete oder wiederholt eingereichte Anfragen abzulehnen.
Warum die Form der Nachricht wichtig ist
Ihr code kümmert sich normalerweise nicht um jeden Eintrag. Es kümmert sich um den Ereignistyp, die Ereignis-ID und das Geschäftselement darin. dataDas ist der Grund, warum gute Handler nur das Parsen, was sie benötigen, und den Rest für die Fehlerbehebung protokollieren.
OpenAPI modelliert diesen Muster direkt. OpenAPI 3.1.0 fügte erstklassige Webhook-Unterstützung mit einer obersten Ebene hinzu, auf der jeder Webhook wie ein Pfad-Element beschrieben wird, aber durch den Provider ausgelöst wird. Die kanonische Beispielnutzung verwendet einen Webhook mit einer webhooks Operation, einem JSON-Anforderungskörper und einer newPet Antwort, um die Empfangsbestätigung anzuzeigen, wie im post Zeige 200 response to indicate receipt, as shown in the OpenAPI Webhook-Beispiel.
Wenn Sie Ihre eigenen Empfänger- oder Anbieterverträge dokumentieren, helfen starke Beispiele mehr als abstrakte Schema-Texte. Ich bevorzuge Referenzen wie SheetMergy’s API Dokumentationsbeispiele weil sie es offensichtlich machen, wie Anforderungsbeispiele, Feldbeschreibungen und erwartete Antworten zusammenpassen.
Eine Webhook ist einfach auf der Transportebene. Die meisten Fehler kommen von fehlenden Annahmen über Kopfzeilen, Körpercodierung oder Signaturregeln.
Webhook-Signaturen sicher überprüfen
Eine signierte Webhook beantwortet eine Frage: Stammte dieser Payload von jemandem, der das gemeinsame Geheimnis kennt?
Dass ist anders als zu fragen, ob der Anfragezeitpunkt aktuell ist oder ob Sie ihn bereits bearbeitet haben. Die Signaturüberprüfung ist der erste Zaun, nicht der letzte.

Die Überprüfungssequenz
Die übliche HMAC-Fluss sieht wie folgt aus:
- Lesen Sie die Signatur aus dem Anbieterkopf.
- Lesen Sie den Rohkörper der Anfrage genau so wie er erhalten wurde
- Laden Sie Ihr Webhook-Secret aus der sicheren Konfiguration.
- Rechnen Sie den erwarteten HMAC mit demselben Algorithmus neu.
- Vergleichen Sie die empfangene Signatur und die berechnete Signatur mit einer zeitunabhängigen Vergleichsmethode.
- Akzeptieren Sie die Anfrage, wenn sie übereinstimmen.
Das Schritt mit dem Rohkörper ist der Punkt, an dem viele sonst gute Implementierungen scheitern. Wenn Ihr Framework den JSON-Code zuerst parsen, die Whitespaces reformieren oder die Kodierungsdetails ändern, bevor Sie das Hashen durchführen, wird Ihre berechnete Signatur nicht mit der des Providers übereinstimmen.
Worauf Sie in Ihrem code achten sollten
Die Fehler, die ich am häufigsten sehe:
- den JSON-Code hashen. Machen Sie das nicht
JSON.stringify(req.body)und erwarten Sie, dass es übereinstimmt. - Verwendung normaler Zeichenfolgengleichheit. Verwenden Sie eine zeitungssichere Vergleichsmethode.
- Geheime Daten hartcodieren. Bewahren Sie sie in Umgebungsvariablen oder einem Geheimnisspeicher auf.
- Sich nur auf Überschriften verlassen. Eine Signaturüberschrift ist nur dann bedeutungsvoll, wenn Sie sie überprüfen.
Für Teams, die die Geheimnisverwaltung zwischen Diensten verschärfen, ist die Anleitung von Capgo zu den API key security for app store compliance relevant, weil dieselbe Disziplin hier gilt. Die geheime Rotation, der skalierte Zugriff und die Vermeidung von Lecks in den Protokollen zählen auch für Webhook-Empfänger.
Ein generischer Verifizierungsbeispiel
const crypto = require('crypto');
function verifySignature(rawBody, receivedSignature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
const a = Buffer.from(receivedSignature, 'utf8');
const b = Buffer.from(expected, 'utf8');
if (a.length !== b.length) return false;
return crypto.timingSafeEqual(a, b);
}
Dies ist absichtlich allgemein gehalten. Realisierte Anbieter prefixieren oft Signatur, kombinieren Zeitstempel in den signierten Inhalt oder kodieren den Digest anders. Die Regel bleibt gleich. Folgen Sie dem genauen Signaturlayout des Anbieters und überprüfen Sie immer gegen den Rohinhalt.
Schutz vor Wiedergabeangriffen
Ein unterschriebener Webhook kann immer noch gefährlich sein, wenn er Stunden später eintrifft und Ihr Handler ihn als neuen behandelt. Das passiert häufiger als Teams erwarten. Proxys loggen den Traffic, Anforderungspayloads gelangen in den falschen Ort oder ein Anbieter wiederholt nach einem Netzwerkfehler und Ihr Endpunkt verarbeitet denselben Ereignis zweimal.

Die Signaturverifizierung beantwortet eine Frage: Hat der Absender diesen Payload mit dem gemeinsamen Geheimnis erstellt? Die Wiedergabeprävention beantwortet eine andere: Sollte diese Anfrage noch jetzt akzeptiert werden? Produktionsempfänger benötigen beide.
Der Mindestcheck, der tatsächlich zählt
Ein praktischer Wiedergabe-Schutz beginnt mit einem unterschriebenen Timestamp. Der Anbieter enthält einen Timestamp in den Headern oder in der unterschriebenen Nachricht und Ihr Empfänger lehnt Anfragen ab, die außerhalb eines kleinen Toleranzfensters liegen.
Dieser Ablauf sollte wie folgt aussehen:
- Lese den Timestamp aus der vom Anbieter definierten PositionGib nicht den Headernamen vor.
- Interpretiere ihn als Ganzzahl oder als RFC-formatierten Datum, basierend auf der Spezifikation des Anbieters.
- Vergleiche ihn mit Ihrem Server-Uhrzeigerschnellgang.
- Anfragen, die zu alt oder zu weit in der Zukunft sind, abweisen.
- Die Zeitstempel als Teil des Signaturschemas überprüfen wenn der Anbieter dies unterstützt
Das letzte Punkt ist wichtig. Wenn der Zeitstempel nicht durch die Signatur abgedeckt ist, kann ein Angreifer einen frischen Zeitstempel einsetzen und den ursprünglichen Body wiederholen. Ich überprüfe immer das genaue Signierungsformat des Anbieters, bevor ich die Zeitstempellogik vertraue
Was wählen Sie für die Toleranzzeitfenster
Fünf Minuten ist eine gängige Voreinstellung. Sie sind kurz genug, um die Angriffszeitfenster zu verkleinern, aber lang genug, um kleine Uhrfehler und normale Netzwerklatenzen zu überstehen
Es gibt ein Gleichgewicht hier. Ein 30-Sekunden-Fenster klingt sicherer, aber es bricht häufiger in realen Systemen, insbesondere wenn Wiederholungen, Warteschlangen oder regionale Latenzen involviert sind. Ein 30-Minuten-Fenster ist einfacher zu bedienen, aber es gibt einem Angreifer viel mehr Zeit, wenn ein signiertes Anfrageexemplar freigegeben wird. Beginnen Sie mit wenigen Minuten, synchronisieren Sie Ihre Server mit NTP und verengen Sie nur, wenn das Liefermuster des Anbieters es unterstützt
Die Wiederholungsabwehr ist nicht nur ein Zeitstempelcheck
Die Zeitstempelüberprüfung blockiert veraltete Anfragen. Sie stoppt jedoch nicht die doppelte Verarbeitung innerhalb des gültigen Fensters. Wenn das gleiche signierte Ereignis zweimal innerhalb dieses Fensters geliefert wird, muss Ihre Anwendung es erkennen
Verwenden Sie eine zweite Ebene:
- Verfolgen Sie Ereignis-IDs oder Liefer-IDs in einem kurzlebigen Speicher wie Redis
- Behandeln Sie Handler als idempotent Dadurch werden wiederholte Lieferungen keine doppelten Bestellungen, E-Mails oder Rechnungsaktionen erzeugen.
- Loggen Sie abgelehnte veraltete Anfragen mit Gründen, aber nie geheime Geheimnisse oder vollständige sensitive Payloads.
- Geben Sie eine schnelle Antwort zurück nach der Validierung und der schweren Arbeit in der Warteschlange an anderer Stelle.
Teams, die sich bereits mit Ablaufzeiten und Ruckholungen beschäftigen, erkennen das Muster wieder. Capgo’s Leitfaden zu Token-Rückholungsmustern in Capacitor-Anwendungen umfasst denselben operativen Gedanken. Ein Kredit- oder eine Anfrage, die einmal gültig war, sollte nicht für immer vertrauenswürdig bleiben.
Gesichert und veraltet ist immer noch gefährlich.
Ein Webhook-Empfänger in Node.js
Node mit Express ist immer noch der schnellste Weg, um einen ernsthaften Empfänger online zu bekommen, aber es gibt einen Haken, der wichtiger ist als jeder andere. Sie benötigen Zugriff auf den Rohkörper, bevor Express ihn in ein Objekt verwandelt.

Ein Produktionsbeispiel mit Express
const express = require('express');
const crypto = require('crypto');
const app = express();
const PORT = process.env.PORT || 3000;
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;
// Capture raw body for signature verification
app.use(
express.json({
verify: (req, res, buf) => {
req.rawBody = buf;
},
})
);
function safeEqual(a, b) {
const aBuf = Buffer.from(a, 'utf8');
const bBuf = Buffer.from(b, 'utf8');
if (aBuf.length !== bBuf.length) return false;
return crypto.timingSafeEqual(aBuf, bBuf);
}
function verifySignature(rawBody, secret, receivedSignature) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return safeEqual(expected, receivedSignature);
}
function isFresh(timestampHeader, toleranceSeconds = 300) {
const timestamp = Number(timestampHeader);
if (!Number.isFinite(timestamp)) return false;
const now = Math.floor(Date.now() / 1000);
return Math.abs(now - timestamp) <= toleranceSeconds;
}
app.post('/webhooks/example', async (req, res) => {
const signature = req.get('x-webhook-signature');
const timestamp = req.get('x-webhook-timestamp');
if (!WEBHOOK_SECRET) {
return res.status(500).send('Webhook secret is not configured');
}
if (!signature || !timestamp) {
return res.status(400).send('Missing required security headers');
}
if (!isFresh(timestamp)) {
return res.status(401).send('Stale webhook');
}
const valid = verifySignature(req.rawBody, WEBHOOK_SECRET, signature);
if (!valid) {
return res.status(401).send('Invalid signature');
}
// Acknowledge quickly
res.status(200).send('OK');
// Process after acknowledgement
try {
const event = req.body;
console.log('Accepted event:', event.event, event.id);
// enqueueJob(event)
} catch (err) {
console.error('Post-ack processing failed:', err);
}
});
app.listen(PORT, () => {
console.log(`Webhook receiver listening on ${PORT}`);
});
Warum diese Struktur hält
Einige Entscheidungen hier sind bewusst getroffen:
- Die Rohdaten werden in Middleware erfasst. Das bewahrt die ursprünglichen Bytes für die Hashierung.
- Die Zeitstempel werden vor der Geschäftslogik überprüft. Es lohnt sich nicht, für veraltete Traffic Arbeit zu leisten.
- Die Route gibt schnell zurück
200. Langlaufende Arbeit gehört in eine Warteschlange oder einen Hintergrundauftrag.Die Post-Ack-Verarbeitung ist isoliert - Die Route gibt schnell zurück. Langlaufende Arbeit gehört in eine Warteschlange oder einen Hintergrundauftrag.. Selbst wenn die Abhängigkeiten fehlschlagen, bleibt der Empfängerpfad klein.
Geheime Informationen sind der Schwachpunkt in vielen Webhook-Implementierungen. Halten Sie sie nicht in der Quelle, fügen Sie sie nicht in Testfällen ein und geben Sie sie nicht in Protokollen wieder. Wenn Sie ein umfassenderes Verfahren für die Rotation und die CI-Verwaltung benötigen, bietet Capgo's Leitfaden zur Verwaltung geheimer Informationen in CI/CD-Pipelines eine gute Lösung für die operative Seite.
Ein kurzer Rundgang hilft, wenn Sie die beweglichen Teile in Aktion sehen möchten:
Was ich für ein lebendiges System ändern würde
Für eine echte Anbieterintegration würde ich eine Doppelung von Ereignis-IDs in der persistenten Speicherung, strukturierte Protokolle mit Anforderungs-IDs und eine Warteschlange hinter dem Bestätigungspfad hinzufügen. Ich würde auch einen einzelnen generischen Endpunkt vermeiden, wenn mehrere Anbieter unterschiedliche Signaturformate verwenden. Trennte Handler sind einfacher zu verstehen und schwerer zu brechen.
Ein Webhook-Empfänger in Python erstellen
Flask ist ein guter Anbieter für einen sauberen Webhook-Beispiel, weil die Anforderungsverarbeitung explizit ist und Pythons Standardbibliothek bereits alles liefert, was Sie für HMAC benötigen.
Das Hauptding, das man beachten muss, ist dasselbe wie in Node. Überprüfen Sie gegen die Rohdaten des Anforderungsbytes, nicht gegen das parsierte JSON-Dictionary.
Ein Flask-Beispiel mit Signatur- und Zeitstempelprüfungen
import os
import time
import hmac
import hashlib
from flask import Flask, request, jsonify
app = Flask(__name__)
WEBHOOK_SECRET = os.environ.get("WEBHOOK_SECRET", "")
def is_fresh(timestamp_header, tolerance_seconds=300):
try:
timestamp = int(timestamp_header)
except (TypeError, ValueError):
return False
now = int(time.time())
return abs(now - timestamp) <= tolerance_seconds
def verify_signature(raw_body, secret, received_signature):
expected = hmac.new(
secret.encode("utf-8"),
raw_body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, received_signature)
@app.route("/webhooks/example", methods=["POST"])
def webhook():
if not WEBHOOK_SECRET:
return "Webhook secret is not configured", 500
signature = request.headers.get("X-Webhook-Signature")
timestamp = request.headers.get("X-Webhook-Timestamp")
if not signature or not timestamp:
return "Missing required security headers", 400
if not is_fresh(timestamp):
return "Stale webhook", 401
raw_body = request.get_data()
if not verify_signature(raw_body, WEBHOOK_SECRET, signature):
return "Invalid signature", 401
payload = request.get_json(silent=True) or {}
# Acknowledge receipt
response = jsonify({"status": "ok"})
# In production, queue payload here instead of heavy sync work
print("Accepted event:", payload.get("event"), payload.get("id"))
return response, 200
if __name__ == "__main__":
app.run(port=5000, debug=True)
Flask-spezifische Details, die zählen
request.get_data() ist der Schlüsselaufruf hier. Es liefert Ihnen die Rohdaten der Körper. Wenn Sie direkt zum request.json, haben Sie bereits die Grenze überschritten, an der sich Unterschiede in der Signatur verwirrend machen.
Einige Implementierungsanmerkungen:
- Verwenden
hmac.compare_digestanstatt der einfachen Gleichheit. - Behandeln Sie fehlende Header als Client-Fehler und lehnen Sie ab. Verwenden
- für die JSON-Verarbeitung
silent=Truewenn Sie die Fehlerbehandlung kontrollieren möchten, anstatt dass Flask eine Ausnahme auslöst. Halten Sie die Route dünn - . Wenn der Payload etwas teuer auslöst, sollten Sie die Arbeit in die Warteschlange einstellen.items
Vermeide es, Debug-Fehlern durch Entspannung von Sicherheitsprüfungen. Debug sie, indem Sie genau die Bytes drucken, die Sie gehasht haben, und genau die Formatierung, die der Anbieter erwartet.
Wo sich die Teams normalerweise verheddern
Die häufige Fehlroute ist das Testen mit einem handgebauschten JSON-Körper, dann das Wechseln zu einem echten Anbieter und das Feststellen, dass die Signatur nicht mehr übereinstimmt. Das bedeutet normalerweise eines von drei Dingen: Der Anbieter signiert einen timestampierten Umschlag, die Signatur ist anders als Sie angenommen haben, oder Middleware hat den Körper vor der Verifizierung geändert.
Wenn das passiert, stoppen Sie das Ändern des Crypto-code zufällig. Fassen Sie die Rohkopfzeilen und den Rohkörper ein, reproduzieren Sie den Hash in einem kleinen isolierten Skript und erstellen Sie ihn dann wieder in der Flask-Routen.
Ein Webhook-Empfänger in Go erstellen
Go ist eine großartige Wahl für Webhook-Empfänger, weil die Standardbibliothek ausreicht. Sie brauchen kein Framework, um einen kleinen, zuverlässigen Handler zu erhalten, und der code ist leicht zu überprüfen.
Etwas zu beachten ist der Körper-Handling. r.Body ist ein Stream. Lesen Sie ihn einmal, hashen Sie die Bytes, die Sie erhalten haben, und dann unmarshallen Sie aus denselben Bytes.
Ein Beispiel aus der Standardbibliothek
package main
import (
"crypto/hmac"
"crypto/sha256"
"crypto/subtle"
"encoding/hex"
"encoding/json"
"io"
"log"
"net/http"
"os"
"strconv"
"time"
)
type WebhookPayload struct {
Event string `json:"event"`
ID string `json:"id"`
Data json.RawMessage `json:"data"`
}
func isFresh(timestampHeader string, toleranceSeconds int64) bool {
ts, err := strconv.ParseInt(timestampHeader, 10, 64)
if err != nil {
return false
}
now := time.Now().Unix()
diff := now - ts
if diff < 0 {
diff = -diff
}
return diff <= toleranceSeconds
}
func verifySignature(rawBody []byte, secret string, received string) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(rawBody)
expected := hex.EncodeToString(mac.Sum(nil))
if len(expected) != len(received) {
return false
}
return subtle.ConstantTimeCompare([]byte(expected), []byte(received)) == 1
}
func webhookHandler(secret string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
signature := r.Header.Get("X-Webhook-Signature")
timestamp := r.Header.Get("X-Webhook-Timestamp")
if signature == "" || timestamp == "" {
http.Error(w, "missing required security headers", http.StatusBadRequest)
return
}
if !isFresh(timestamp, 300) {
http.Error(w, "stale webhook", http.StatusUnauthorized)
return
}
rawBody, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "failed to read body", http.StatusBadRequest)
return
}
if !verifySignature(rawBody, secret, signature) {
http.Error(w, "invalid signature", http.StatusUnauthorized)
return
}
var payload WebhookPayload
if err := json.Unmarshal(rawBody, &payload); err != nil {
http.Error(w, "invalid json", http.StatusBadRequest)
return
}
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK"))
log.Printf("accepted event=%s id=%s", payload.Event, payload.ID)
}
}
func main() {
secret := os.Getenv("WEBHOOK_SECRET")
if secret == "" {
log.Fatal("WEBHOOK_SECRET is not set")
}
http.HandleFunc("/webhooks/example", webhookHandler(secret))
log.Println("listening on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
Warum Go hier sicher wirkt
Ein paar Vorteile fallen auf: Der Handler ist explizit
- Die Handler ist explizit. Keine versteckte Middleware-Magie.
- Typing hilft an den Rändern.. Die Header-Verarbeitung, die Zeitstempelumrechnung und die JSON-Entschlüsselung funktionieren alle klar.
- Die Standardkryptopakete reichen aus.. Keine zusätzliche Abhängigkeit für die grundlegende HMAC-Verifizierung.
Betriebsnotizen.
Wenn der Webhook-Volumen wächst, bietet Go's Konkurrenzmodell Ihnen Platz, um Hintergrundarbeit ohne Änderung Ihres HTTP-Eingangs zu verteilen. Selbst dann sollten Sie den Empfänger eng halten. Akzeptieren, validieren, bestätigen und dann weitergeben.
Die stärksten Go-Webhook-Handler, die ich gesehen habe, bleiben langweilig. Sie vermischen nicht die Transportverifizierung mit der Geschäftslogik und sie tun keine Datenbank-schweren Arbeit vor der Antwort zurück.
Wichtige Debugging-Techniken.
Ein Webhook-Bug zeigt sich normalerweise als Support-Nachricht und nicht als Stack-Trace. Der Anbieter sagt, sie hätten das Ereignis geliefert. Ihr Endpunkt sagt, nichts habe das Anwendungsprogramm erreicht, oder die Signaturverifizierung sei auf einer Anfrage gescheitert, die auf den ersten Blick wie eine gültige Anfrage aussieht. An diesem Punkt ist Debugging daran, die genaue HTTP-Austausch, Byte für Byte, wiederherzustellen und zu beweisen, wo es gebrochen ist.

Ein praktisches Debugging-Toolkit.
Beginnen Sie mit der Drahtformatvorlage.
Wenn eine Signaturprüfung fehlschlägt, fangen Sie den Rohanforderungskörper genau so auf, wie er erhalten wurde, zusammen mit den zur Verifizierung verwendeten Kopfzeilen. In der Praxis ist der Fehler oft langweilig. Ein Framework hat JSON vor der Hashierung geparst, ein Proxy hat die Kodierung geändert oder ein Testwiedergabe hat den ursprünglichen Timestamp-Kopfzeile verpasst. Die Protokollierung des geparsten Objekts reicht nicht aus. Sie benötigen die ursprünglichen Bytes und die Verifizierungsinputs.
Diese Werkzeuge helfen, das Problem schnell zu isolieren:
- RohanforderungsaufzeichnungLogge Kopfzeilen, Inhaltstyp, Inhaltslänge und den unveränderten Körper während der Ermittlung.
- AnforderungsbetrachtungsendpunkteDienste wie
webhook.sitebestätigen, was der Absender übermittelt hat. - Lokale Tunnelung.
ngrokund ähnliche Werkzeuge ermöglichen Ihnen, gegen einen lokalen Empfänger zu testen, während der Anbieter im Bilde ist. - Manuelle WiedergabeRekonstruieren Sie die Anforderung mit
curloder Postman mit demselben Körper und Kopfzeilen. Das ist der schnellste Weg, um zu bestätigen, ob Ihr code oder der Anbieter-Payload das Problem ist. - LieferantenlieferprotokolleDer Absender-Dashboard umfasst oft Antwortcodes, Wiederholungsverlauf und Anforderungsidentifikatoren, die Sie gegen Ihre Protokolle abgleichen können.
Die Muster sind wichtig. Arbeiten Sie von außen nach innen. Überprüfen Sie zunächst, ob der Anbieter das erwartete gesendet hat. Dann überprüfen Sie, ob Ihr Server die gleichen Bytes erhalten hat. Dann überprüfen Sie, ob Ihr code die gleichen Bytes mit den gleichen Geheimnissen und Zeitstempelregeln gehasht hat.
Protokollierung, die wirklich hilft
Gute Webhook-Protokolle sollten drei Fragen in einer Suche beantworten:
| Frage | Nützliches Protokollfeld |
|---|---|
| Wurde die Anfrage eingegangen? | route, method, received_at |
| Warum wurde sie abgelehnt? | missing_header, stale_timestamp, signature_failed |
| Kann ich es später korrelieren? | event_id, provider_request_id |
Ein vierter Feld hilft in realen Systemen. Fügen Sie ein lokales request_id durch Ihren Empfänger generiert, damit Sie den Anforderungsverlauf durch Ihre App, Warteschlange und Arbeiter-Logs verfolgen können.
Seien Sie wählerisch, was Sie speichern. Loggen Sie niemals Geheimnisse. Vermeiden Sie es, vollständige Produktionslasten zu speichern, wenn sie Kunden-Daten, Zugriffstoken oder Rechnungsdaten enthalten. Ein sichereres Muster ist, Metadaten plus einen kurzen Body-Hash zu speichern. Das lässt Ihnen immer noch die Wiederholung von Fehlern und die Überprüfung, ob zwei Lieferungen identisch waren, zu.
Reproduzieren Sie Fehlfälle mit den ursprünglichen Eingaben
Dies ist der Teil, den grundlegende Tutorials auslassen. Wenn Sie den fehlgeschlagenen Anforderungsaufruf nicht genau wiederholen können, raten Sie.
Speichern Sie einen fehlgeschlagenen Webhook als:
- rohe Bytes des Körpers
- alle signaturbezogenen Header
- Anforderungszeitstempel
- Inhalts-Typ
- Anbieteranforderungs-ID
Wiederhole es dann gegen einen Testendpunkt. Wenn die Wiederholung erfolgreich ist, vergleiche, was sich im Transit geändert hat. Häufige Täter sind Middleware, die die Anforderungskörper normalisiert, Encoding-Missverständnisse und Lastenausgleichs-Server, die Kopfzeilen streichen oder umschreiben. Ich habe auch Fälle gesehen, in denen Teams Payloads aus pretty-printeten Dashboard-Ansichten kopierten, anstatt den tatsächlichen Anforderungskörper. Der Unterschied im Whitespaces allein war ausreichend, um die HMAC-Verifizierung zu brechen.
Für eine breitere Veröffentlichung und mobile Transportfehlerbehebung zeigt sich das gleiche Debugging-Diskussionsstil in der Anleitung von Capgo zu Werkzeugen zur Fehlersuche bei OTA-Updates in Capacitor. Ein anderer Transport, dieselbe Lektion. Fange den tatsächlichen Anforderungsweg vor dem Ändern der Anwendung code ein.
Wenn die Signaturverifizierung fehlschlägt, inspiziere die Rohdaten, die genauen Kopfzeilen, die bei der Verifizierung verwendet wurden, und den Timestamp-Wert, bevor du dich mit der Kryptographie code auseinandersetzt.
Ein Überprüfungscheck für Produktionsreife von Webhooks
Ein Webhook-Handler sieht normalerweise in der Staging-Umgebung gut aus, bis zum ersten Wiederholungssturm, dem fehlerhaften Payload oder dem Signaturmismatch um 2 Uhr morgens. Die Produktionsbar ist höher. Der Empfänger muss gefälschte Anforderungen ablehnen, legitime Wiederholungen akzeptieren und Operatoren genügend Signal geben, um Fehlern nachzugehen, ohne sensible Daten preiszugeben.
Sicherheits- und Korrektheitsprüfungen
- Verifiziere jeden Anforderungssignatur. Endpunkte veröffentlichen URLs. Test-URLs werden in Chat geteilt. Die Signaturverifizierung ist der Kontrolle, die dir sagt, dass der Absender den geteilten Geheimcode kannte.
- Ablehne alte Anforderungen. Ein gültiges Signatur auf einem alten Payload kann immer noch wiederholt werden. Stellen Sie eine Toleranz für den Zeitstempel ein, die der Anbieter-Retry-Modell entspricht.
- Hashen Sie den Rohkörper, nicht das parsierte JSON.. Middleware kann Schlüssel umstellen, Leerzeichen normalisieren oder die Kodierung ändern. Die Verifizierung muss gegen die genauen Bytes durchgeführt werden, die eingegangen sind.
- Halten Sie Signaturschlüssel außerhalb von code.. Umgebungsvariablen sind ein Grundlevel. Ein Secrets-Manager ist ein besseres Angebot, wenn Sie regelmäßig Anmeldeinformationen rotieren oder über mehrere Umgebungen laufen.
- Fehler schließen bei Auth-Fehlern.. Wenn der Signaturheader fehlt, falsch formatiert ist oder eine unerwartete Scheme verwendet, lehnen Sie die Anfrage ab und loggen Sie die Gründe.
Zuverlässigkeitsprüfungen.
- Bestätigen Sie schnell.. Anbieter behandeln normalerweise jede 2xx als Erfolg, also überprüfen Sie die Anfrage, speichern Sie, was Sie benötigen, und bewegen Sie langsame Arbeit in eine Warteschlange oder einen Arbeiter.
- Machen Sie Handler idempotent.. Das gleiche Ereignis kann mehr als einmal eintreffen. Verschieben Sie Seiteneffekte auf ein Ereignis-ID, eine Liefer-ID oder einen anderen stabilen Anbieter-Bezeichner.
- Prädiktive Fehlercodes zurückgeben. Verwenden Sie
400für fehlerhaftes Eingabedaten,401oder403kontext: HTML-Textfragment aus einem längeren Capgo-UI-String (Elternschlüssel `alternatives_cta_questions`). Seite/Bereich: Vergleichsseite für lebend-updated Capacitor-Alternativen. Rolle: Langer Marketing- oder Rechtsparagraph. Gesehen in: Seite alternatives.astro. Produkt- und Markenbezeichnungen von Capgo sowie Entwicklertermen bleiben genau erhalten. Nachrichtenschlüssel `alternatives_cta_questions` (Alternativen-CTA-Fragen). | HTML-Textfragment aus einem längeren Capgo-UI-String (Elternschlüssel `appflow_cta_questions`). Seite/Bereich: Appflow-Vergleichs-/Migrationsmarketing. Rolle: Langer Marketing- oder Rechtsparagraph. Gesehen in: Seite ionic-appflow.astro. Produkt- und Markenbezeichnungen von Capgo sowie Entwicklertermen bleiben genau erhalten. Nachrichtenschlüssel `appflow_cta_questions` (Appflow-CTA-Fragen). | HTML-Textfragment aus einem längeren Capgo-UI-String (Elternschlüssel `capwesome_cta_questions`). Seite/Bereich: Capawesome-Vergleichsseite. Rolle: Langer Marketing- oder Rechtsparagraph. Gesehen in: Seite capwesome.astro. Produkt- und Markenbezeichnungen von Capgo sowie Entwicklertermen bleiben genau erhalten. Nachrichtenschlüssel `capwesome_cta_questions` (Capwesome-CTA-Fragen). | HTML-Textfragment aus einem längeren Capgo-UI-String (Elternschlüssel `consulting_faq_subtitle`). Seite/Bereich: Beratungsleistungen. Rolle: Untertitel oder Slogan. Gesehen in: Seite consulting.astro. Produkt- und Markenbezeichnungen von Capgo sowie Entwicklertermen bleiben genau erhalten. Nachrichtenschlüssel `consulting_faq_subtitle` (Beratungs-FAQ-Untertitel). | Seite/Bereich: Appflow-Vergleichs-/Migrationsmarketing. Rolle: Kurzer UI-Label oder Navigationselement. Gesehen in: Seite ionic-appflow.astro, Seite ionic-enterprise-plugins.astro, Seite solutions/ionic-enterprise-plugins.astro. Nachrichtenschlüssel `appflow_plugins_or` (Appflow-Plugins-oder).5xxfehlerhaftes Verifizierungsdatum und - nur dann, wenn Ihr System das Problem ist. Dies erleichtert das Verständnis der Wiederholungsverhalten des Providers.Setze Grenzen vor der Analyse
- . Setze die Größe der Anforderung, den Inhaltstyp und die Anzahl der Header frühzeitig. Dies verhindert, dass ein Webhook-Endpunkt in einen allgemeinen Eingangsschacht verwandelt wird.. Accept only the fields and event types you support. Loose parsing feels convenient at first and becomes expensive during provider API changes.
. Akzeptieren Sie nur die Felder und Ereignistypen, die Sie unterstützen. Loose Parsing fühlt sich zunächst bequem an und wird während der Änderungen des Providers teuer.
Überwachungsprüfungen durchführen
Verwenden Sie das Standard:
- Folgen Sie der Empfangsbestätigung, der Verifizierung und der Bearbeitung als separate Ergebnisse.
- Loggen Sie die Anforderungs-IDs, die Ereignis-IDs, den Status der Signatur und den Zeitstempelversatz.
- Messungen von Warteschlangenverzögerungen, Handlerlatenz und Wiederholungsvolumen.
- Halten Sie einen sicheren Wiedergabe-Weg für Staging- oder Wiederholungsarbeitsabläufe bereit.
- Warnen Sie auf Änderungen von Mustern, wie z.B. ein Anstieg von Signaturfehlern oder Duplikate von Lieferungen
Capgo ist ein nützliches Beispiel für den breiteren operativen Punkt. Es umfasst Werkzeuge rund um die Lieferung und die Beobachtbarkeit in seinem Update-Workflow und Teile seines Ökosystems berühren auch Webhook-bezogene Flüsse. Die Lektion ist praktisch. Lieferungssysteme benötigen Sichtbarkeit von der Empfangsbestätigung bis zur Beendigung
Wenn ein Team die oben genannten Kontrollen abdeckt, ist der Webhook-Receiver in der Regel für die Produktion in gutem Zustand. Wenn ein Item fehlt, zeigt sich dieser Riss normalerweise während eines Vorfalls und nicht während der Demo
Häufig gestellte Fragen zu Webhooks
Welchen Status code sollte ich zurückgeben?
Geben Sie eine 2xx sobald Sie den Webhook akzeptiert haben. Wenn die Validierung fehlschlägt, sollten Sie einen Client- oder Auth-Fehler zurückgeben, der der Fehlschlag entspricht, wie z.B. 400 für fehlerhaftes Eingabedaten oder 401 für ungültige Authentifizierungsdaten. Halten Sie diese Logik konsistent, damit die Anbieter-Dashboards einfacher zu interpretieren sind.
Soll ich den Webhook synchron verarbeiten?
Normalerweise nicht. Validieren Sie ihn, bestätigen Sie ihn, und schieben Sie dann die tatsächliche Arbeit in eine Warteschlange oder einen Hintergrundarbeiter. Das hält die Lieferungspfad schnell und reduziert duplicate Wiederholungsversuche, die durch langsames Downstream-Verarbeitung verursacht werden.
Wie soll ich mit Wiederholungsversuchen umgehen?
Ziehen Sie sie an. Bauen Sie Idempotenz in Ihren Handler ein, damit das Empfangen des gleichen Ereignisses nicht zu doppelten Nebeneffekten führt. Ereignis-IDs oder Anbieterlieferungs-IDs sind die üblichen Anker dafür.
Was passiert, wenn Ereignisse außerhalb der Reihenfolge ankommen?
Entwickeln Sie Handler, die bei Bedarf tolerant gegenüber der Reihenfolge sind. Wenn das Geschäftsprozess eine Sequenz erfordert, speichern Sie genug Zustände, um veraltete Übergänge zu erkennen, anstatt anzunehmen, dass die Lieferungspfad die Ereignisreihenfolge widerspiegelt.
Wie gehe ich mit Änderungen der Webhook-Version um?
Versionieren Sie Ihre Handler-Logik absichtlich. Halten Sie die Anbieter-spezifische Parsen isoliert, vermeiden Sie, dass Sie Payload-Voraussetzungen durch Ihr Codebase streuen, und fügen Sie Tests mit echten gefangenen Beispielen hinzu, bevor Sie die Unterstützung für eine neue Format rollen.
Wenn Ihr Team Capacitor oder Electron-Anwendungen bereitstellt, Capgo Es lohnt sich, darüber zu erfahren, weil es Teams ermöglicht, signierte Web-Updates sicher zu liefern, das Rollout-Verhalten zu beobachten und von Fehlern ohne Wartezeit auf die App-Store-Überprüfung zu profitieren. Dies passt zum gleichen Ingenieursinstinkt hinter soliden Webhook-Designs: Eingaben validieren, Freigabe-Pfade beobachten und Wiederherstellung beschleunigen.