Skip to content

Webhook Security Overhaul: Timestamped Versioned Signatures, Replay Windows, Dual-Secret Rotation, Delivery Idempotency, Dead-Letter Re-Drive #338

Description

@Christopherdominic

Summary

The outbound webhook signing scheme in src/webhooks/signature.rs is inadequate for a payments-adjacent system:

  • The signature is a bare HMAC-SHA256 hex of the payload — there is no timestamp binding, so any captured delivery can be replayed indefinitely and receivers have no spec-compliant way to reject stale deliveries.
  • There is no scheme versioning (cf. Stripe's t=<ts>,v1=<sig> format), so the algorithm can never be rotated without breaking every consumer.
  • There is no secret rotation story — a leaked webhook secret requires a hard cutover that breaks all receivers simultaneously.
  • The doc comment claims the signature proves the request "came from Nova Launch" — a copy-paste artifact from another codebase that must go.
  • Delivery retries (src/webhooks/retry.rs) carry no idempotency/delivery ID, so receivers cannot deduplicate redelivered events.

Task

  1. Implement a versioned signature header: X-TipJar-Signature: t=<unix_ts>,v1=<hmac_hex> where the signed message is "{t}.{raw_body}".
  2. Add a documented tolerance window (default 5 min) and publish receiver-side verification guidance (including constant-time comparison) in docs/, with reference verification snippets.
  3. Support dual signing during secret rotation: multiple active secrets produce v1=<sig1>,v1=<sig2>; add secret versioning to storage and an admin rotation endpoint with audit logging.
  4. Add a stable X-TipJar-Delivery-Id (UUID, persisted) and X-TipJar-Event-Type header to every delivery; the same delivery ID must be reused on retries so receivers can deduplicate.
  5. Retry hardening in retry.rs: exponential backoff with jitter, per-endpoint circuit breaking after N consecutive failures (reuse services/circuit_breaker.rs), dead-letter persistence of permanently failed deliveries with an admin re-drive endpoint.
  6. Exhaustive tests: signature format, tolerance boundary (±1s around the window), dual-secret rotation overlap, delivery-ID stability across retries, circuit-breaker transitions.

Acceptance criteria

  • Replayed payloads outside the tolerance window are rejectable by receivers using documented verification
  • Secret rotation demonstrably works with zero missed verifications during overlap (test proves it)
  • Delivery IDs stable across retries; dead-lettered deliveries re-drivable
  • "Nova Launch" reference removed; receiver docs published
  • All checks pass

PR requirements (mandatory)

  • ✅ Your PR must pass all checks — PRs with failing or skipped checks will not be merged.
  • 📸 You must attach a screenshot in the PR description demonstrating the result (for this issue: a captured webhook request showing the new headers, plus the passing test run).
  • 🔗 You must link this issue number in your PR description (e.g. Closes #<issue-number>). PRs without a linked issue will not be reviewed.

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial Campaign | FWC26Campaign: Official Campaign | FWC26difficulty: very hardRequires deep expertise and substantial effortsecuritySecurity hardening or vulnerability work

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions