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.

Jetzt bei Google Play Laden im App Store