Join our Newsletter — 33% off our NHI Course
Home› Guides› Webhook Secrets Security Guide: Signing, Verifying and Rotating…
Guide Non-Human Identity (NHI)

Webhook Secrets Security Guide: Signing, Verifying and Rotating Webhook Credentials

PDF for newsletter subscribers · free to read online

← All guides
By NHI Mgmt Group, edited by Lalit Choda Updated 26 October 2026 15 min read
On this page

A webhook endpoint is a public URL that changes data in your systems, and the only thing standing between it and a forged request is usually one shared secret. Payment, source control, messaging and SaaS providers push events to receivers that fulfil orders, trigger builds, grant access or update records, so an unverified or leaked webhook secret lets anyone on the internet drive those actions. This guide covers how webhook senders and receivers authenticate each other, the signature schemes used by GitHub, Stripe, Slack and Twilio and the vendor-neutral Standard Webhooks specification, how to store and rotate signing secrets without dropping events, and what to log and measure. It is written for application security engineers, platform teams and the owners of integrations who hold these secrets. The Stripe-specific API key side is covered in the sibling Stripe API Keys Security Guide.

Key takeaways

  • A webhook secret is a non-human credential. It authenticates a machine (the sender) to another machine (your receiver), so it needs an owner, an inventory entry, a storage location and a rotation schedule like any other secret.
  • Verify the signature over the raw body, every time. Every major provider signs the exact bytes it sent with HMAC-SHA256 or an asymmetric key; parsing or re-serialising the JSON before verification breaks the check, and skipping it lets an attacker forge events.
  • Signatures without timestamps and IDs still allow replay. Check the signed timestamp against a tolerance (five minutes is the common default) and deduplicate on the delivery or event ID, because a captured valid request can otherwise be resent.
  • Rotation needs an overlap window. Stripe and Slack keep the previous secret valid for up to 24 hours and Standard Webhooks signs with old and new keys in parallel; design receivers to accept multiple secrets so rotation is routine rather than an outage.
  • Some providers reuse the account credential as the signing key. Twilio signs with the account auth token, so a leaked signing key is also a leaked API key, and rotating one rotates the other. Know which model each provider uses before you plan incident response.

How identity and access work in webhooks

A webhook inverts the usual API call: the provider is the client and your application is the server. Your receiver must answer two identity questions on every request: did this really come from the provider, and is it a fresh, unmodified message? TLS on your endpoint authenticates you to the provider, not the provider to you, so sender authentication has to come from something else. Providers use four mechanisms, often in combination.

  • Shared-secret HMAC signature: the provider computes an HMAC over the payload (and usually a timestamp) with a per-endpoint secret and sends it in a header. This is the dominant model.
  • Asymmetric signature: the provider signs with a private key and publishes the public key, so the receiver holds nothing secret. Standard Webhooks defines this as the v1a scheme using ed25519.
  • Mutual TLS: the provider presents a client certificate at the TLS layer. Slack offers this as an alternative to signature checking, presenting a certificate with the SAN platform-tls-client.slack.com that your TLS terminator must match exactly. See the mTLS Guide.
  • Network origin: GitHub publishes its delivery addresses through the GET /meta API and Stripe publishes a list of sending IPs. Allowlisting reduces noise but is a supporting control: it does not prove message integrity and the ranges change.

Provider signature schemes compared

Provider or specHeaderAlgorithm and signed contentReplay protectionSigning key
GitHubX-Hub-Signature-256 (value prefixed sha256=)HMAC-SHA256 hex digest of the raw payload. Legacy X-Hub-Signature uses HMAC-SHA1 and exists only for compatibility.No signed timestamp; use the unique X-GitHub-Delivery ID (a redelivery keeps the same value).Per-webhook secret you choose
StripeStripe-Signature (t= timestamp, v1= signatures)HMAC-SHA256 over timestamp.payload. Only v1 is a valid live scheme; ignore other schemes to prevent downgrade.Signed timestamp, library default tolerance 5 minutes; dedupe on event ID.Per-endpoint whsec_ secret, different for test and live mode
SlackX-Slack-Signature (v0=), X-Slack-Request-TimestampHMAC-SHA256 over v0:timestamp:bodyReject if timestamp differs from local time by more than 5 minutesApp signing secret (replaces the deprecated verification token)
TwilioX-Twilio-SignatureHMAC-SHA1 over the full URL plus POST parameters sorted alphabetically; JSON bodies are covered by a bodySHA256 query parameterNot timestamp-based; rely on idempotent handlingThe account auth token (also an API credential)
Standard Webhookswebhook-id, webhook-timestamp, webhook-signaturev1: HMAC-SHA256 over msg_id.timestamp.payload; v1a: ed25519Signed timestamp with a receiver-chosen window; webhook-id as idempotency keywhsec_ secret of 24 to 64 random bytes, base64; or whsk_/whpk_ key pair

Shared secret or asymmetric key

Use HMAC when the provider only offers it, which is most of the time. Prefer an asymmetric scheme when you are the sender and many customers will verify your events: with ed25519 the receiver stores only a public key, so a breach of a receiver cannot be used to forge events to anyone else, and your own key store is the only place a signing key lives. Consider mTLS when the receiver sits behind infrastructure that already terminates client certificates and you want verification out of application code, accepting that it moves the trust decision to the TLS layer and its header forwarding.

Admin roles and privileged access

Whoever can configure a webhook can redirect events, read the signing secret and, on many platforms, trigger redeliveries. Treat webhook administration as privileged access on both sides of the integration.

  • Provider-side configuration rights: in GitHub, repository and organisation admins can create and edit webhooks and set secrets; in Stripe, users who can reach the Webhooks tab in Workbench can reveal and roll endpoint secrets. Review who holds those roles and remove standing access where a deployment pipeline can do the job. See the GitHub and GitHub Actions Security Guide.
  • Secret reveal is a sensitive event: a person who copies a signing secret from a dashboard can forge events indefinitely until it is rotated. Limit the number of people who ever see the value, and prefer flows where the secret goes straight from the provider API into a secrets manager.
  • Endpoint URL changes: changing the payload URL silently sends business data (payment events, repository contents, chat messages) to a new destination. Alert on URL changes and require change approval.
  • Receiver-side access: the people and pipelines that can read the receiver's secret store entry, or change its verification code, can disable the control. Code review for the verification path and least-privilege policies on the secret path (NIST SP 800-53 AC-6) matter as much as the provider console.

Integrations, API tokens and service accounts

Webhook secrets rarely travel alone. A typical receiver also holds an outbound API key to fetch the full object (Stripe thin events require a follow-up API call, for example), and sometimes credentials for downstream systems it updates. Map those relationships before you decide how to store and rotate anything.

CredentialWho holds itImpact if leakedWhere to storeRotation trigger
Webhook signing secret (HMAC)Provider and receiverForged events accepted as genuine: orders fulfilled, access granted, builds triggeredSecrets manager, injected at runtime; never in code or the payload URLScheduled, staff change, suspected exposure, repository leak
Asymmetric signing private keySender onlyForgery to every receiver that trusts the public keyKMS or HSM-backed key storeScheduled, key compromise
Auth token used as signing key (Twilio model)Provider and receiverForged webhooks and full API access to the accountSecrets manager with tight read scopeAny exposure: rotate as an API credential incident
Outbound API key used by the receiverReceiverDirect API abuse, data access, refunds or payoutsSecrets manager, restricted key scopesPer provider key policy; see the API Key Management Guide
  • One secret per endpoint and environment: Stripe already issues different secrets for test and live mode and per endpoint; follow the same pattern for every provider so a leaked staging secret cannot forge production events.
  • Keep the payload URL free of secrets: GitHub's guidance is explicit that API keys and credentials do not belong in the payload URL. URLs end up in proxy logs, provider delivery logs and screenshots.
  • Treat the receiver as a service account: give the verification service its own identity in the secrets manager, scoped to the webhook secrets it needs. See the Secrets Management Guide and the Cloud Secrets Managers Guide.
  • When you are the sender: customer-supplied webhook URLs are a server-side request forgery surface. The Standard Webhooks specification recommends sending through a filtering proxy that blocks internal addresses and running webhook workers in a subnet with no access to internal services; the OWASP SSRF cheat sheet adds disabling redirects, validating resolved IPs to defeat DNS rebinding and blocking cloud metadata endpoints such as 169.254.169.254. See the SSRF Prevention Guide.

Verifying deliveries correctly

Use the provider's official library where one exists; otherwise implement these steps in order.

  1. Capture the raw body before any framework parsing. Stripe states that any manipulation of the raw body causes verification to fail, and GitHub warns that proxies or load balancers that alter the payload or headers break the check. Disable body-rewriting middleware on the webhook route.
  2. Select the right header and scheme. Use X-Hub-Signature-256, not the SHA-1 header, for GitHub; accept only v1 for Stripe; reject requests where the signature header is missing rather than treating absence as "unsigned but allowed".
  3. Rebuild the signed string exactly. Concatenate the timestamp, ID and body in the provider's order and separator (. for Stripe and Standard Webhooks, : for Slack).
  4. Compute the HMAC and compare in constant time. GitHub, Stripe and Slack all require a constant-time comparison such as Node.js crypto.timingSafeEqual or Python hmac.compare_digest, never ==.
  5. Accept any one of several valid signatures. During rotation Stripe and Standard Webhooks send one signature per active secret; verification should succeed if any of them matches a secret you hold.
  6. Check freshness. Reject timestamps outside the tolerance. Stripe warns that a tolerance of 0 disables the recency check entirely, and recommends NTP so server clock drift does not cause false rejections.
  7. Deduplicate. Record processed event or delivery IDs (webhook-id, Stripe event ID, X-GitHub-Delivery) and skip repeats. Stripe does not guarantee ordering and its created field has one-second resolution, so do not use timestamps to detect duplicates.
  8. Acknowledge fast, process asynchronously. GitHub ends the connection if there is no 2xx within 10 seconds; queue the verified event and return immediately, then validate the event type and action before acting on it.

A valid signature proves origin, not authorisation: check that the referenced account or repository belongs to the tenant the endpoint serves, and for high-impact actions such as payouts or access grants re-fetch the object from the provider API rather than trusting payload fields, the pattern Stripe's thin events are built around.

Rotating webhook secrets without downtime

ProviderRotation mechanismOverlap windowReceiver requirement
StripeRoll secret in the Webhooks tab of WorkbenchExpire immediately or delay up to 24 hours; one signature per active secret during overlapAccept a list of secrets; remove the old one after expiry
SlackRegenerate signing secret in the app settingsSlack documents a 24-hour overlap for the client secret; plan the signing secret change as a coordinated deployDeploy the new secret promptly after regeneration
GitHubEdit the webhook and set a new secretNo documented overlap: the new secret applies to subsequent deliveriesAccept old and new secrets briefly, then redeliver any failed deliveries

A safe rotation sequence: (1) add the new secret to the receiver's accepted list and deploy; (2) roll the secret at the provider, using its overlap option where available; (3) watch verification failure rates and the provider's delivery log; (4) remove the old secret from the receiver; (5) redeliver anything that failed. Rollback is simply re-adding the previous secret while the provider overlap is still open, which is why step 1 comes first. Automate this as a runbook; see the Secrets Rotation Runbook Template.

Logging and detection

  • Verification outcomes: log every rejection with reason (missing header, bad signature, stale timestamp, duplicate ID), source IP, endpoint and provider. A burst of bad signatures from a non-provider address is probing; a sudden rise from provider addresses usually means a secret mismatch after rotation or a body-altering proxy.
  • Never log secrets or signatures: log a key identifier so you can tell which secret matched.
  • Provider audit trails: collect GitHub organisation audit log events for webhook creation and changes, and Stripe endpoint changes and secret rolls, into the SIEM. Alert on new endpoints, URL changes and secret reveals outside a change window.
  • Secret exposure: scan repositories, CI logs and container images for whsec_ prefixes and provider secret formats. See the Git Secret Scanning Guide.

Metrics that show the control works

  • Verification coverage: percentage of webhook routes that enforce signature, timestamp and deduplication checks (target 100%).
  • Secret age: days since last rotation per endpoint, against your policy (for example 90 or 180 days).
  • Rejected request rate: rejections per thousand deliveries, split by reason, with an alert threshold.
  • Owner coverage: percentage of webhook endpoints with a named owner and inventory record.
  • Time to rotate after exposure: hours from a detected leak to the old secret being invalid.

Hardening baseline

  1. Inventory first: list every inbound webhook (provider, endpoint URL, secret location, owner, actions triggered) and every outbound webhook you send. The Service Account Inventory Template can hold these records.
  2. Pilot verification in log-only mode: for endpoints that do not verify today, compute and log the verification result without rejecting for one to two weeks, fix false failures (usually raw body handling), then enforce. Roll back by switching the route back to log-only, not by removing the code.
  3. Move secrets into a manager: replace hard-coded and environment-file secrets with runtime retrieval; generate high-entropy secrets (Standard Webhooks specifies 24 to 64 random bytes) where you choose them.
  4. Enforce transport: HTTPS only with valid certificates. Stripe accepts TLS 1.2 and 1.3 only, and Twilio will not connect to self-signed certificates; keep GitHub's SSL verification enabled.
  5. Subscribe to the minimum events: both GitHub and Stripe recommend it.
  6. Add network filtering as a second layer: provider IP allowlists at the edge, refreshed automatically from GET /meta or the provider list.
  7. Schedule rotation: automate it with the overlap sequence above and record each rotation.

Small vs large organisations: a small team with a handful of integrations gets most of the value from official verification libraries, a secrets manager and a yearly rotation. A large organisation with hundreds of endpoints should centralise receipt behind a webhook gateway that verifies, deduplicates and queues events for internal consumers, so verification logic lives in one reviewed place and internal services never see provider secrets.

Framework mapping

ConcernNIST SP 800-53 Rev. 5NIST CSF 2.0ATT&CK v20
Authenticating the sending serviceIA-9 Service Identification and AuthenticationPR.AA-03T1078 Valid Accounts (forged or stolen credentials)
Secret generation, storage and rotationIA-5 Authenticator Management, SC-12 Cryptographic Key Establishment and ManagementPR.AA-01T1552.001 Credentials In Files
Integrity and freshness of each messageSC-8 Transmission Confidentiality and Integrity, SC-23 Session AuthenticityPR.DS-02T1557 Adversary-in-the-Middle
Validating payload and referenced objectsSI-10 Information Input Validation, AC-3 Access EnforcementPR.AA-05
Logging verification failures and config changesAU-2 Event Logging, AU-12 Audit Record GenerationDE.CM-09

In ISO/IEC 27001:2022 Annex A the closest controls are 5.17 Authentication information, 8.24 Use of cryptography and 8.15 Logging.

Common misconfigurations

  • Verification behind a feature flag that is off: code paths such as "if a secret is configured, verify" (the pattern in Stripe's own sample) silently accept everything when the environment variable is missing. Fail closed when the secret cannot be loaded.
  • Comparing with string equality: a non-constant-time comparison leaks timing information about the expected signature. Every provider document reviewed here calls for constant-time comparison.
  • Tolerance set to zero or very large: setting Stripe's tolerance to 0 disables the freshness check, and a tolerance of hours makes replay practical. Keep it near five minutes and fix clock drift with NTP instead.
  • Accepting legacy schemes: verifying GitHub's SHA-1 header or Stripe's v0 test signature in production reopens downgrade paths.
  • Secrets committed with the handler: signing secrets copied into source, sample configs or CI variables printed in logs. Credentials published by mistake can stay valid for a long time: in the Home Depot Token Exposure 2025 a GitHub token published online stayed live for more than a year. A webhook secret leaked the same way keeps working until someone rotates it.
  • One shared secret across environments and endpoints: a single value reused everywhere means one leak forges events to every receiver and rotation needs a coordinated change across all of them.

Practitioner checklist

  • Inventory every inbound and outbound webhook with owner, secret location and triggered actions.
  • Verify signatures over the raw body with the provider library or a constant-time HMAC check on every route.
  • Enforce a timestamp tolerance of about five minutes and deduplicate on the event or delivery ID.
  • Store each signing secret in a secrets manager, one secret per endpoint and environment, never in the payload URL.
  • Build receivers that accept multiple secrets and rotate on a schedule using the provider overlap window.
  • Fail closed if the secret cannot be loaded; reject unsigned or legacy-scheme requests.
  • Log verification failures by reason and alert on webhook URL changes and secret reveals.
  • Re-fetch objects from the provider API before high-impact actions and check tenant ownership.
  • When sending webhooks, route through an SSRF-filtering proxy and consider ed25519 signatures.

Standards and references

Related NHI Mgmt Group resources: Secrets Management Guide · Stripe API Keys Security Guide · SaaS API Token Security Guide · API Key Management Guide · Secrets Rotation Runbook Template

PDF for subscribers

Take this guide with you

Every section, table and checklist in one PDF, with links to the sources and related guides. Subscribe to our weekly NHI & AI Identity Journal and we’ll email you a download link.

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Researched and written by the NHI Mgmt Group editorial team using AI tools, under the editorial direction of Lalit Choda. Facts are drawn from the sources listed on this page and checked by our automated quality process. Last updated 26 October 2026. Spotted an error? Use our contact page.
NHI Mgmt Group, the independent authority on Non-Human Identity, IAM and Agentic AI security. nhimg.org

PDF Downloads for Newsletter Subscribers

Get the guide

Enter your details and we’ll email you a download link.

The full guide is free to read online. The PDF is a thank you for subscribing.

Fill in all fields marked * and tick the PDF box to continue.