A webhook URL is a public network endpoint, not an authentication credential. Before a payload can create an opportunity, mark an invoice paid, or send a customer message, the receiver must prove that the provider signed the exact bytes received. Signature verification is only the first boundary; replay protection, idempotency, secret handling, and controlled dispatch are separate requirements.
Implement the provider's signing contract exactly
Do not assume every provider signs the same material or uses the same header format. Some sign the raw body, while others sign a timestamp plus the body or include a version prefix. Capture the request body as bytes before JSON middleware transforms it, read the documented signature and timestamp headers, and construct the signed message exactly as the provider specifies. Reject a missing or unsupported signature version before parsing business fields.
Authenticate the original bytes first. Parsing, normalization, and business validation happen only after the signature passes.
Compare decoded signatures without leaking comparison position
Decode the received and computed signatures into the same byte representation. Check algorithm, encoding, and length before calling a constant-time comparison primitive; Node.js `crypto.timingSafeEqual`, for example, requires equal-length buffers. Treat malformed hex, base64, prefixes, or multiple signature values as explicit validation cases rather than allowing conversion errors to become server failures.
Reject stale deliveries and make accepted events idempotent
When the provider signs a timestamp, enforce a tolerance derived from its documentation and your clock-skew policy. Timestamp checks reduce replay exposure but do not replace idempotency because legitimate providers retry. Persist the provider's delivery or event ID with a uniqueness constraint before performing side effects. A duplicate should return the provider-appropriate success response while skipping already completed work.
Separate authentication from business authorization
A valid signature proves origin and integrity, not that every requested action is acceptable. Validate event type, account or tenant identifier, object state, currency, amounts, and allowed transitions after authentication. Fetch authoritative state from the provider for high-impact events when the threat model requires it. Route unknown event versions to quarantine instead of guessing their schema.
Acknowledge quickly and dispatch through a durable boundary
After authentication and minimal envelope validation, persist the accepted event and return the response required by that provider. Process slower CRM, messaging, and reporting work asynchronously. Record correlation ID, signature version, event ID, attempt, and terminal state without logging secrets or unnecessary personal data. This preserves evidence when a downstream integration fails after the provider has been acknowledged.
Rotate secrets according to provider behavior
Secret rotation is provider-specific. If the platform supports overlapping active secrets or emits multiple signatures, accept only the documented overlap during a short migration window. Otherwise deploy the new receiver secret in coordination with the provider's rotation step and monitor failures closely. Store secrets in a managed secret store, identify them by non-secret version metadata, and remove the previous secret after the overlap and retry window have ended.
Test the rejection and recovery paths
Use provider fixtures when available, then test a changed byte, missing header, wrong secret, malformed encoding, unequal signature length, stale and future timestamp, duplicate event ID, body-parser mutation, unknown event version, queue outage, and secret rotation. Alert on signature-failure rate, timestamp rejection, duplicate delivery, queue age, and quarantined events. Do not include raw secrets or complete sensitive payloads in alerts.
