Skip to content

Webhook Signature Verification SDK: Language Libraries, Test Vectors & Interactive Verifier #72

Description

@grantfox-oss

Overview

Every developer who integrates Fluxa webhooks must implement HMAC-SHA256 signature verification. Getting this wrong means silently accepting forged webhook payloads — a critical security vulnerability. Fluxa currently documents the signature format in prose but provides no reference implementations. This issue produces official verification SDK snippets in five languages, a set of published test vectors (known payload + secret → known signature) that developers can use to validate their implementation, and an interactive verifier in the Fluxa dashboard.

What needs to be built

internal/webhook/ — Signature generation hardening

  • Add: X-Fluxa-Timestamp header (Unix seconds at delivery time) — to prevent replay attacks
  • Verification algorithm developers must implement:
    1. Check |now - X-Fluxa-Timestamp| < 300 (5-minute tolerance) — reject stale deliveries
    2. Construct signed_payload = X-Fluxa-Timestamp + "." + raw_body
    3. Compute expected = HMAC-SHA256(signed_payload, webhook_secret)
    4. Compare expected to X-Fluxa-Signature using a constant-time comparison
  • Update all existing webhook deliveries to include X-Fluxa-Timestamp
    docs/webhook-verification/ — Reference implementations

One file per language, each < 50 lines, production-quality:

  • verify.ts — TypeScript (Node.js crypto module, no external dependencies)
  • verify.py — Python (hmac + hashlib stdlib, no external dependencies)
  • verify.go — Go (crypto/hmac + crypto/sha256 stdlib)
  • verify.rb — Ruby (openssl stdlib)
  • verify.php — PHP (hash_hmac + hash_equals)
    Each file includes: the verification function, inline comments explaining each step, a usage example, and a note about constant-time comparison and why it matters.

docs/webhook-verification/test-vectors.json

  • 10 test vectors: { secret, timestamp, body, expectedSignature } — generated using the Fluxa reference implementation

  • Vectors include: normal payload, empty body, Unicode body, large body (10KB), and a stale timestamp (> 5 minutes old, expected result: rejected)
    internal/webhook/ — API

  • POST /v1/webhooks/verify — accepts { secret, timestamp, body, signature }; runs the verification algorithm; returns { valid: boolean, reason: string | null }; rate limited to 60 requests per minute per IP
    Dashboard — Webhook endpoint settings page

  • "Verify Signature" tool: paste area for raw request headers (auto-extracts X-Fluxa-Signature and X-Fluxa-Timestamp), paste area for body, secret input (masked), "Verify" button

  • Result: green "Valid" or red "Invalid" with the failure reason

  • "Copy code snippet" button per language

Acceptance criteria

  • All 5 language implementations produce the correct expectedSignature for all 10 test vectors — confirmed by running each implementation against the full vector set
  • Stale timestamp (> 5 minutes) is rejected by the verification function in all 5 languages — not just by the API endpoint
  • POST /v1/webhooks/verify correctly returns valid: false with reason: 'stale_timestamp' for a timestamp 6 minutes in the past
  • All implementations use constant-time comparison — confirmed by code review (no string equality == on the signature values)
  • X-Fluxa-Timestamp header is present on all new webhook deliveries — confirmed by inspecting the webhook_delivery_attempts table's stored headers
  • Rate limit of 60 requests/minute is enforced — the 61st request within a minute returns 429

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardThird CampaignCampaign: Third CampaignbackendBackend / API workblockchainStellar / blockchain workgood first issueGood for newcomers

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions