Skip to content
Prividium

Verifying Signatures

Signature verification confirms a delivery:

  • Originated from this service
  • Was not modified in transit
  • Is recent (replay protection)

Standard Webhooks references:

Headers

Each webhook request includes case-insensitive headers:

  • webhook-id
  • webhook-timestamp
  • webhook-signature

Header Semantics

  • webhook-id: Unique per event, stable across retries, used for idempotency.
  • webhook-timestamp: Unix timestamp (seconds), used for replay checks.
  • webhook-signature: One or more signatures (space-delimited), including rotation overlap cases.

Signature Format

webhook-signature: v1,<sig1> v1,<sig2>
  • Each signature is v1,<base64>.
  • Digest algorithm: HMAC-SHA256.

Signing Input

<webhook-id>.<webhook-timestamp>.<raw-payload>

Verification Steps (Production)

  1. Read the raw request body bytes.
  2. Extract webhook-id, webhook-timestamp, and webhook-signature.
  3. Enforce a timestamp tolerance window (recommended: +/- 5 minutes).
  4. For each active signing key, compute v1 HMAC over the signing input.
  5. Compare with constant-time equality.
  6. Accept the request if any signature verifies.
  7. Deduplicate using webhook-id.

Reference

Single key:

WEBHOOK_SECRET="whsec_..." cargo run

Multiple keys (rotation overlap):

WEBHOOK_SECRET="whsec_old,whsec_new" cargo run

Common Pitfalls

  • Parsing and re-serializing JSON before verification.
  • Using non-constant-time string comparison.
  • Enforcing only one signature during key rotation.
  • Ignoring timestamp tolerance checks.