Webhook Signature Verification and Replay Defense

Webhook signature verification and replay defense requires raw-body validation, secret rotation, timestamp checks, delivery deduplication, and idempotency.

Webhook authenticity and business authorization are separate decisions

Webhook signature verification and replay defense begins with the exact bytes received. Many providers calculate HMAC or another signature over the raw request body, sometimes including a timestamp. Parsing and reserializing JSON before validation changes the byte sequence and can break correct verification.

GitHub webhook validation guidance recommends a high-entropy secret, HMAC-SHA256, raw payload validation, and constant-time comparison. Provider schemes differ; follow the versioned documentation for each source rather than inventing one generic header format.

After authenticity, validate event type, account or tenant, target resource, expected state transition, and local permission. A compromised provider account can send correctly signed but unauthorized business actions.

Log validation outcomes without retaining secrets or sensitive bodies

Record provider, endpoint, delivery ID, event type, tenant, timestamp header, receive time, signature version, secret key identifier, verification result, freshness result, deduplication result, authorization decision, processing outcome, latency, and a keyed payload fingerprint. Avoid logging live secrets and minimize payload fields.

This is authored receiver telemetry:

{"provider":"github","delivery_id":"d-81f...","event":"deployment_status",
 "received_at":"2026-11-10T16:17:04Z","signature_version":"sha256",
 "secret_id":"webhook-prod-v3","signature_valid":true,"fresh":true,
 "duplicate":false,"authorized":true,"effect":"deployment_record_updated"}

Preserve the raw body only in a restricted evidence path when incident need justifies it. A payload can contain personal data, source details, tokens, or business records.

Reject stale deliveries and make valid retries harmless

If the provider signs a timestamp, enforce a bounded tolerance after accounting for clock synchronization and documented retry behavior. Store provider delivery IDs or a keyed fingerprint for at least the retry and attack window. Reject an old timestamp even when the signature is valid.

GitHub webhook security best practices explicitly addresses replay attacks and unique delivery identifiers. Providers can legitimately redeliver after timeouts, so processing must be idempotent: use conditional updates, unique constraints, state-version checks, and transactional outboxes rather than assuming one delivery.

An attacker can replay within the accepted time window. Deduplication and idempotency remain necessary even with timestamp validation.

Detect validation failures and unexpected valid events

Alert on missing or malformed signature headers, unknown key IDs, constant verification failures, stale timestamps, repeated delivery IDs, payload-size anomalies, unsupported event types, tenant mismatch, authorization denials, and valid events that cause unusual high-impact transitions.

Separate internet noise from provider errors and attack behavior. A burst of invalid signatures may be scanning; one valid signature using a retired secret may indicate leakage or incomplete rotation. Join receiver evidence with provider delivery logs, secret access, configuration changes, network metadata, and resulting business records.

Define fields and semantics through security telemetry data contracts. Logging only HTTP 200 cannot show which security gates passed.

Rotate secrets without accepting unsigned fallback

Store webhook secrets through managed secrets management, use separate secrets per provider, endpoint, tenant, and environment, and restrict secret read access. During rotation, accept only explicitly identified current and previous keys for a short overlap.

Terminate TLS at a controlled boundary that preserves raw body and signature headers. Rate-limit before expensive processing, but perform cryptographic validation before parsing or queuing trusted work. Restrict accepted event types and maximum size.

Test valid, invalid, stale, duplicate, reordered, Unicode, proxy-modified, key-overlap, and idempotency cases using boundary and regression tests. Never test with production billing or deployment consequences.

Prove one webhook cannot create a duplicate effect

Next step: choose one non-production webhook. Record the signing scheme, raw-body boundary, secret owner, rotation procedure, timestamp tolerance, delivery ID, deduplication retention, authorized events, and idempotency key.

Send one valid authored event, resend the identical delivery, and send one stale event through the provider’s supported test mechanism. Confirm only the first valid delivery changes state and every decision is logged. The durable control is authenticated bytes, bounded freshness, local authorization, and safe retry behavior.

Frequently asked questions

Does a valid webhook signature mean the requested action is authorized?

No. It authenticates the message under the provider’s signing scheme. The consumer must still validate event type, tenant, resource, state transition, freshness, and local authorization.

Is source-IP allowlisting enough for webhook security?

No. Provider ranges change, proxies obscure sources, and IP does not protect payload integrity. Use provider-supported cryptographic validation, with network restrictions as an additional layer.