Webhook-Sicherheit und Signaturprüfung
23. Juli 2026 · 4 Min. Lesezeit
Ein Webhook-Endpoint ist eine Tür im öffentlichen Internet. Ohne Prüfung weißt du nicht, ob der Request von Stripe kam oder von jemandem, der deine URL erraten hat – und „im Test lief es“ sagt darüber nichts aus.
Die Bedrohung, konkret
Dein Endpoint nimmt {"event":"payment.succeeded","amount":9999} an. Schaltet er daraufhin ein Produkt frei, bekommt jeder, der auf deine URL posten kann, Gratis-Produkte. Signaturprüfung macht aus „jemand behauptet“ ein „der Anbieter sagt“.
Wie HMAC-Signaturen funktionieren
Der Anbieter gibt dir ein Signing Secret. Bei jeder Zustellung berechnet er mit diesem Secret einen Hash über den rohen Request-Body und schickt ihn in einem Header (Stripe-Signature, X-Hub-Signature-256, X-Signature). Du berechnest denselben Hash und vergleichst. Stimmen sie überein, ist zweierlei bewiesen: Der Absender kennt das Secret, und der Body wurde nicht verändert.
const crypto = require("crypto");
function verify(rawBody, header, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody) // ← der ROHE Body, nicht das geparste Objekt
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(header)
);
}
Die drei Fehler, die fast alle machen
- Den geparsten Body hashen.
JSON.stringify(req.body)ist nicht byte-identisch mit dem, was ankam – Schlüsselreihenfolge und Leerzeichen unterscheiden sich. Du musst den rohen Body behalten (in Express:express.raw()für diese Route). - Mit
===vergleichen. Ein String-Vergleich bricht beim ersten unterschiedlichen Zeichen ab und verrät damit Timing-Informationen. Nimm einen timing-sicheren Vergleich. - Den Zeitstempel ignorieren. Ohne Frische-Prüfung lässt sich ein abgefangener gültiger Request beliebig wiederholen. Stripe stellt
t=in den Signatur-Header – alles, was älter als ein paar Minuten ist, ablehnen.
Was einen Endpoint sonst schützt
- Eine nicht erratbare URL. Kein Ersatz für Signaturen, hält aber Scanner fern.
- Nur HTTPS – sonst reist der Payload im Klartext.
- IP-Allowlists, wo der Anbieter Bereiche veröffentlicht (GitHub und Stripe tun das).
- Rate-Limiting, damit eine Flut von Requests den Endpoint nicht umwirft.
- Den Payload auch nach der Prüfung als nicht vertrauenswürdige Eingabe behandeln – es bleiben nutzerbeeinflusste Daten.
Wie viel davon brauche ich?
Das hängt vollständig davon ab, was der Webhook auslöst. Gewährt er Zugang, bewegt er Geld oder schreibt er in eine Datenbank: alles oben. Lässt er nur dein Handy klingeln – wie bei einem Webhooky-Endpoint, wo der schlimmste Fall eine unerwünschte Benachrichtigung ist und das Löschen des Endpoints die URL sofort tötet – sind eine lange Zufalls-URL und HTTPS eine angemessene Antwort. Pass den Aufwand den Konsequenzen an.
Mehr: Webhook-Best-Practices · Was ist ein Webhook
Hol dir Webhooky
Kostenlos für deine ersten 100 Benachrichtigungen – Endpoint in zwei Minuten eingerichtet.