Webhook key rotation starts with the provider's exact signed-message definition. Preserve raw request bytes, use the provider's supported verifier, accept retiring keys only within the documented overlap policy and separate signature acceptance from event deduplication. Two providers can both use HMAC-SHA256 while signing different byte sequences and carrying different replay metadata.
Raw bytes are part of the contract
Read the body once at the framework boundary in the form required by the verifier before parsing JSON. Equivalent JSON objects can have different whitespace, escaping and key order, so parsing and reserializing changes the signed bytes. Keep verification before business processing and use a constant-time comparison when implementing the documented low-level scheme. Prefer an official library when available rather than reconstructing a provider's envelope from memory.
GitHub documents its X-Hub-Signature-256 HMAC over the payload with a sha256= prefix. Stripe's webhook guidance uses its own signature header and timestamped verification contract. These are distinct provider protocols; do not add a generic timestamp/body concatenation rule to GitHub or omit Stripe's documented timestamp handling.
GitHub: exact webhook payload signature verification
Stripe: signature errors and endpoint operations
Compare two harmless synthetic providers
For a fixture, provider A signs the exact body bytes. Provider B signs an explicitly documented timestamp delimiter and body. Use local disposable secrets and a body such as a fictional record update. Verify the untouched bytes, then change one harmless whitespace character. Both schemes should reject an altered signed message unless the fixture re-signs it. This demonstrates byte sensitivity without delivering any external request.
# Illustrative synthetic schemes, not drop-in provider verifiers.
message_a = raw_body
message_b = timestamp.encode('ascii') + b'.' + raw_body
# Compute each HMAC with its disposable fixture key.
# Use the real provider SDK for real endpoint verification.These local schemes have not been executed here. They are intentionally labeled synthetic so the example cannot be mistaken for exact vendor integration code. A production fixture must use the selected provider's actual header parsing, supported algorithms, encoding and clock tolerance. Record SDK version and endpoint mode; test-mode and live-mode signing secrets are separate configurations.
| Case | Expected boundary | Separate assertion |
|---|---|---|
| Current key, unchanged body | Accept by provider scheme | Schema validation |
| Retiring key in overlap | Provider-policy outcome | Record key version |
| Body whitespace altered | Signature refusal | No business processing |
| Old key after retirement | Reject | No new acceptance window |
Rotation has a timeline and an owner
Inventory endpoints and consumers, provision the replacement in approved secret storage and establish when the provider will use it. If the provider supports overlapping active and retiring keys, verify both only during that documented interval. If it does not, plan the cutover and expected retry behavior explicitly. Do not manufacture an overlap promise by keeping old secrets accepted forever.
Record which key version verified a delivery without logging the secret, raw signature or private body. A provider-issued version identifier is preferable to an improvised secret fingerprint. Retire the old verifier after the documented window and confirm a fixture signed only by that key is refused. The rotation record should identify who can reverse a mistaken configuration and what restoring the old key would mean for exposure and delivery.
Replay and duplicate effects remain separate
A valid signature establishes integrity and the relevant secret possession under the provider scheme. It does not automatically establish freshness or make a repeated event harmless. Apply authenticated timestamp checks where the protocol provides them, and deduplicate stable event or business identities according to the provider contract. A header not covered by the signature should not be treated as authenticated replay metadata.
OWASP's webhook guidance describes these controls as related but distinct. After verification, validate the event schema and durably accept work before acknowledging under the chosen delivery policy. No provider rotation or live webhook fixture was executed for this guide. The release artifact should include current-key, retiring-key, altered-body, expired-window and duplicate-event outcomes using synthetic records, with no reusable credentials in logs.
OWASP: webhook authenticity, replay and idempotent processing
Stripe webhook idempotency for SaaS subscriptions
Observability for small teams: start with the failed customer task
Bring one API or data workflow and a sanitized failure example for a bounded implementation assessment.
How do you rotate webhook signing keys safely?
Follow each provider’s exact signed-byte and overlap contract, verify before processing and test key retirement separately from replay and business deduplication.
Can all HMAC webhook providers use the same message format?
No. Signed bytes, headers, timestamps and rotation overlap differ by provider.
Does a valid signature prevent duplicate processing?
No. Authenticated repeated events still need durable deduplication and business idempotency.
