Webhook-Signatur-Validator
Dein Anbieter sendet einen Signatur-Header, dein Handler sagt, er passt nicht, und die Doku antwortet mit einem SDK zum Installieren. Das sagt dir nicht, warum. Füg unten Signatur, Secret und Rohbody ein: Wir berechnen den HMAC in deinem Browser neu und probieren bei einem Fehlschlag die üblichen Ursachen durch — ein Leerzeichen im Secret, ein abschließender Zeilenumbruch, ein neu serialisierter Body, ein veralteter Timestamp.
Kurz gesagt: Eine Webhook-Signatur scheitert fast immer an einem von fünf Gründen — falsches Secret (Test vs. Live), ein vom Framework neu serialisierter Body, ein Timestamp außerhalb des 5-Minuten-Fensters, ein abschließender Zeilenumbruch oder ein übrig gebliebenes Präfix sha256= / v0=. Dieses Tool berechnet den HMAC neu und sagt dir, welcher es ist.
Füg eine fehlschlagende Signatur ein
Die vier Signaturschemata
Alle vier sind HMAC, unterscheiden sich aber darin, was gehasht und wie das Ergebnis kodiert wird. Ein falsches Detail, und jede Zustellung schlägt fehl.
| Anbieter | Was signiert wird | Algorithmus |
|---|---|---|
| Stripe | HMAC von t.body; Signatur und Timestamp in einem Stripe-Signature-Header | SHA-256 hex |
| GitHub | HMAC des Rohbodys; Signatur in X-Hub-Signature-256 | SHA-256 hex |
| Shopify | HMAC des Rohbodys; Signatur in X-Shopify-Hmac-Sha256 | SHA-256 base64 |
| Slack | HMAC von v0:timestamp:body; braucht auch den Timestamp-Header | SHA-256 hex |
Twilio ist die Ausnahme — es hasht eine kanonische URL plus sortierte Parameter, nicht den Body. Nutze dafür den Twilio-Signatur-Validator.
Die fünf echten Ursachen
Sortiert danach, wie oft sie in Support-Threads auftauchen.
| # | Ursache | Symptom | Lösung |
|---|---|---|---|
| 1 | Falsches Secret | Jede Zustellung scheitert, auch eine frische | Test-vs.-Live-Verwechslung — Anbieter vergeben pro Modus und pro Endpoint ein anderes Signing-Secret. |
| 2 | Neu serialisierter Body | Scheitert, nachdem das Framework das JSON geparst hat | Signiere die Rohbytes, nicht JSON.stringify(parsed): Abstände und Schlüsselreihenfolge ändern den Hash. |
| 3 | Veralteter Timestamp | Klappt im Test, scheitert bei erneut gesendeten Events | Stripe und Slack lehnen alles über 5 Minuten ab. Sende kein Stunden altes Event erneut. |
| 4 | Abschließender Zeilenumbruch | Body um ein Byte daneben, Signatur nie korrekt | Ein Proxy oder Editor hat einen Zeilenumbruch angehängt. Das Tool erkennt genau diesen Fall. |
| 5 | Präfix wörtlich verglichen | sha256= oder v0= im Vergleich enthalten | Vergleiche nur den hex-/base64-Teil, nicht das Schema-Präfix. |
Die Rohbody-Falle
Die häufigste Ursache: Du prüfst gegen den geparsten und neu serialisierten Body statt gegen die exakten Bytes. JSON.stringify(JSON.parse(body)) ändert Abstände und Schlüsselreihenfolge, sodass der HMAC nicht mehr passt. Lies den Rohbody, bevor irgendetwas ihn parst.
// Express: keep the RAW bytes, do NOT let JSON re-serialise them
app.post('/webhook', express.raw({ type: '*/*' }), (req, res) => {
const raw = req.body; // Buffer, exactly as received
verify(raw, req.headers['stripe-signature'], secret);
});# Flask: request.get_data() returns the raw body; request.json does NOT raw = request.get_data() # bytes, untouched verify(raw, request.headers['X-Hub-Signature-256'], secret)
// Next.js App Router: read the text before parsing
export async function POST(req) {
const raw = await req.text(); // sign THIS, not JSON.stringify(await req.json())
verify(raw, req.headers.get('stripe-signature'), secret);
}Faustregel: Signatur und Rohbytes am Eingang erfassen, prüfen, dann parsen.
Timestamps und Replays
Stripe und Slack integrieren einen Timestamp in den signierten String und lehnen alles über fünf Minuten ab — das ist Replay-Schutz, kein Bug. GitHub und Shopify tun das nicht, eine gültige GitHub-Signatur bleibt also für immer gültig. Zeigt das Tool gültig, aber abgelaufen, sind Secret und Body in Ordnung; du sendest nur ein altes Event erneut. Erzeuge eine frische Zustellung, statt eine erfasste erneut zu senden.
Sobald die Signatur stimmt
Du willst ein korrekt signiertes Test-Event an deinen eigenen Endpoint senden? Der Webhook-Signer baut eines für alle sechs Anbieter. Um echte Zustellungen zu erfassen und zu inspizieren, leite sie an einen Relay-Tunnel.
Häufige Fragen
Speichert ihr mein Signing-Secret?
Nein. Der HMAC wird in deinem Browser mit Web Crypto berechnet; Secret und Body erreichen unsere Server nie. Öffne den Netzwerk-Tab: Beim Klick auf Prüfen geht kein Request raus.
Welche Anbieter werden unterstützt?
Stripe, GitHub, Shopify und Slack — die vier HMAC-über-Body-Schemata. Twilio signiert stattdessen eine kanonische URL und hat deshalb ein eigenes Tool: den Twilio-Signatur-Validator.
Hier klappt es, aber mein Server lehnt trotzdem ab. Warum?
Fast immer der Body. Dein Framework hat das JSON geparst, und du hast es vor der Prüfung neu serialisiert, was Abstände und Schlüsselreihenfolge ändert. Signiere die Rohbytes — siehe die Express-, Flask- und Next-Snippets oben.
Was bedeutet „gültig, aber abgelaufen“?
Der HMAC ist korrekt, aber Stripe und Slack lehnen Signaturen über fünf Minuten zusätzlich ab, um Replays zu blockieren. Wenn du ein Event erfasst und später erneut sendest, scheitert die Prüfung am Timestamp, obwohl das Secret stimmt.