WebhookToolkit
Stripe · GitHub · Shopify · Slack

Webhook-handtekening-validator

Laatst bijgewerkt: juli 2026

Je provider stuurt een handtekening-header, je handler zegt dat die niet klopt, en de docs antwoorden met een te installeren SDK. Dat vertelt je niet waarom. Plak hieronder de handtekening, je secret en de ruwe body: we herberekenen de HMAC in je browser en proberen bij falen de gebruikelijke oorzaken — een spatie te veel in het secret, een afsluitende regeleinde, een opnieuw geserialiseerde body, een verlopen timestamp.

Kort gezegd: een webhook-handtekening faalt bijna altijd om één van vijf redenen — verkeerd secret (test vs live), een door je framework opnieuw geserialiseerde body, een timestamp buiten het venster van 5 minuten, een afsluitende regeleinde of een overgebleven prefix sha256= / v0=. Deze tool herberekent de HMAC en zegt welke.

Plak een falende handtekening

Alles draait in je browser met de Web Crypto API. Je signing-secret wordt nooit naar onze servers gestuurd — controleer het in het Netwerk-tabblad.

De vier handtekening-schema's

Alle vier zijn HMAC, maar ze verschillen in wat er gehasht wordt en hoe het resultaat gecodeerd is. Eén detail fout en elke levering faalt.

ProviderWat wordt ondertekendAlgoritme
StripeHMAC van t.body; handtekening en timestamp in één Stripe-Signature-headerSHA-256 hex
GitHubHMAC van de ruwe body; handtekening in X-Hub-Signature-256SHA-256 hex
ShopifyHMAC van de ruwe body; handtekening in X-Shopify-Hmac-Sha256SHA-256 base64
SlackHMAC van v0:timestamp:body; heeft ook de timestamp-header nodigSHA-256 hex

Twilio is de uitzondering — het hasht een canonieke URL plus gesorteerde parameters, niet de body. Gebruik daarvoor de Twilio-handtekening-validator.

De vijf echte oorzaken

Gerangschikt naar hoe vaak ze in support-threads opduiken.

#OorzaakSymptoomOplossing
1Verkeerd secretElke levering faalt, zelfs een verseTest-vs-live-verwarring — providers geven per modus en per endpoint een ander signing-secret.
2Opnieuw geserialiseerde bodyFaalt nadat je framework de JSON parsteOnderteken de ruwe bytes, niet JSON.stringify(parsed): spatiëring en sleutelvolgorde veranderen de hash.
3Verlopen timestampSlaagt in tests, faalt bij opnieuw verzonden eventsStripe en Slack weigeren alles ouder dan 5 minuten. Stuur geen uren oud gevangen event opnieuw.
4Afsluitende regeleindeBody één byte scheef, handtekening nooit goedEen proxy of editor voegde een regeleinde toe. De tool markeert precies dit geval.
5Prefix letterlijk vergelekensha256= of v0= in de vergelijking meegenomenVergelijk alleen het hex/base64-deel, niet de schema-prefix.

De ruwe-body-valkuil

De belangrijkste oorzaak: je verifieert tegen de geparste-dan-opnieuw-geserialiseerde body in plaats van de exacte bytes. JSON.stringify(JSON.parse(body)) verandert spatiëring en sleutelvolgorde, dus de HMAC klopt niet meer. Lees de ruwe body voordat iets hem parst.

Express
// 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
# 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
// 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);
}

Vuistregel: vang de handtekening en de ruwe bytes aan de rand, verifieer, dan pas parsen.

Timestamps en replays

Stripe en Slack verweven een timestamp in de ondertekende string en weigeren alles ouder dan vijf minuten — dat is replay-bescherming, geen bug. GitHub en Shopify niet, dus een geldige GitHub-handtekening blijft voor altijd geldig. Zegt de tool geldig maar verlopen, dan zijn je secret en body prima; je stuurt gewoon een oud event opnieuw. Genereer een verse levering in plaats van een gevangen event opnieuw te sturen.

Zodra de handtekening klopt

Een correct ondertekend testevent naar je eigen endpoint sturen? De webhook-signer bouwt er één voor alle zes providers. Om echte leveringen te vangen en te inspecteren, richt ze op een Relay-tunnel.

Signer openenTwilio signature validatorRelay

Veelgestelde vragen

Bewaren jullie mijn signing-secret?

Nee. De HMAC wordt in je browser berekend met Web Crypto; het secret en de body bereiken onze servers nooit. Open het Netwerk-tabblad: bij klikken op Controleren gaat er geen verzoek uit.

Welke providers worden ondersteund?

Stripe, GitHub, Shopify en Slack — de vier HMAC-over-body-schema's. Twilio ondertekent in plaats daarvan een canonieke URL en heeft dus een eigen tool: de Twilio-handtekening-validator.

Het slaagt hier maar mijn server weigert het toch. Waarom?

Bijna altijd de body. Je framework parste de JSON en je serialiseerde die opnieuw vóór verificatie, wat spatiëring en sleutelvolgorde verandert. Onderteken de ruwe bytes — zie de Express-, Flask- en Next-snippets hierboven.

Wat betekent 'geldig maar verlopen'?

De HMAC klopt, maar Stripe en Slack weigeren ook handtekeningen ouder dan vijf minuten om replays te blokkeren. Als je een event ving en het later opnieuw stuurt, faalt de verificatie op de timestamp ook al klopt het secret.

Webhook-handtekening-validator: Stripe, GitHub, Shopify, Slack · Webhook Toolkit