Webhook Signatures

Every webhook sent by Hook Mesh includes a cryptographic signature that proves its authenticity. This prevents attackers from forging webhooks to your customers' endpoints.

Why Signatures Matter

Without signatures, anyone could send fake webhooks to your customers' endpoints:

  • Unauthorized actions: Trigger operations without authentication
  • Data injection: Insert malicious or incorrect data
  • Replay attacks: Resend old webhooks to cause duplicate processing
  • Service disruption: Flood endpoints with fake webhooks

Hook Mesh's signature system ensures that webhooks can only be sent by you, and that your customers can verify their authenticity before processing.

How Signatures Work

Hook Mesh uses HMAC-SHA256, an industry-standard cryptographic algorithm used by Stripe, GitHub, Shopify, and other major platforms.

Signature Generation Process

1

Construct signed content

webhook_id.timestamp.json_payload

Concatenate the webhook ID, timestamp, and payload

2

Compute HMAC-SHA256

HMAC-SHA256(secret, signed_content)

Use the endpoint's secret as the HMAC key

3

Base64 encode

base64(hmac_result)

Convert binary result to ASCII string

4

Add version prefix

v1,base64_signature

Prefix with version for future algorithm changes

Webhook Headers

Hook Mesh includes three security headers with every webhook:

HeaderExamplePurpose
Webhook-Idmsg_2Zy3X8...Unique webhook identifier
Webhook-Timestamp1705756800Unix timestamp (seconds)
Webhook-Signaturev1,a1b2c3...HMAC-SHA256 signature
Example Webhook Request
POST /webhooks HTTP/1.1
Host: api.customer.com
Content-Type: application/json
User-Agent: HookMesh-Delivery/1.0
Webhook-Id: msg_2Zy3X8qP9rK5mN1vB
Webhook-Timestamp: 1705756800
Webhook-Signature: v1,ZjQxNmY3MGViYTc4NDExYWJiZGU0ZjA5ZTU4MTY0NzU=

{
  "event_type": "user.created",
  "user_id": "usr_123",
  "email": "alice@example.com",
  "created_at": "2026-01-20T15:30:00Z"
}

Endpoint Secrets

Each endpoint has its own unique signing secret. Secrets are:

  • 64 characters: Hexadecimal format for high entropy
  • Cryptographically random: Generated using secure random bytes
  • Per-endpoint: Isolates security between customers
  • Rotatable: Can be changed without downtime
Secret Format
whsec_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8
Secret prefix: Secrets start with whsec_ (webhook secret) to prevent accidental exposure in logs or error messages.

Timestamp Tolerance

Hook Mesh includes a timestamp in each signature to prevent replay attacks. Your customers should:

  • Check timestamp freshness: Reject webhooks older than 5 minutes
  • Allow clock skew: Account for small time differences between servers
  • Store processed IDs: Track webhook IDs to prevent duplicate processing
Timestamp Validation Example
function isTimestampValid(timestamp) {
  const webhookTime = parseInt(timestamp, 10);
  const currentTime = Math.floor(Date.now() / 1000);
  const tolerance = 5 * 60; // 5 minutes in seconds

  // Reject if webhook is too old
  if (currentTime - webhookTime > tolerance) {
    return false;
  }

  // Reject if webhook is from the future (clock skew)
  if (webhookTime - currentTime > tolerance) {
    return false;
  }

  return true;
}

// Example usage
const timestamp = req.headers['webhook-timestamp'];
if (!isTimestampValid(timestamp)) {
  return res.status(400).json({ error: 'Webhook timestamp invalid' });
}

Signature Versioning

Signatures are prefixed with a version number (v1) to support algorithm changes in the future:

v1,ZjQxNmY3MGViYTc4NDExYWJiZGU0ZjA5ZTU4MTY0NzU=
│   │
│   └─ Base64-encoded signature
└───── Version (currently always v1)

If Hook Mesh ever changes the signature algorithm, the version will be incremented (v2, v3, etc.), allowing your customers to support multiple versions during migration.

Security Comparison

ProviderAlgorithmSignature Format
Hook MeshHMAC-SHA256v1,base64
StripeHMAC-SHA256t=timestamp,v1=sig
SvixHMAC-SHA256v1,base64
GitHubHMAC-SHA256sha256=hex
Industry standard: Hook Mesh uses the same HMAC-SHA256 algorithm as Stripe, Svix, Shopify, and other leading platforms.

Common Security Mistakes

MistakeImpactFix
Not verifying signaturesCriticalAlways verify before processing
Ignoring timestampHighReject old webhooks (5 min tolerance)
Hardcoding secretsHighUse environment variables
Logging signaturesMediumSanitize logs, don't log secrets
String comparisonMediumUse timing-safe comparison

Implementation Guide

Ready to implement signature verification? We provide complete code examples in multiple languages:

Verifying Signatures Guide

Step-by-step implementation with working code in Node.js, Python, Go, and PHP.

View Implementation GuideArrow pointing right

Best Practices

  • Always verify signatures: Never process unverified webhooks
  • Validate timestamps: Reject webhooks older than 5 minutes
  • Use timing-safe comparison: Prevent timing attacks on signature validation
  • Store secrets securely: Use environment variables or secret managers
  • Rotate secrets regularly: Change secrets every 90 days for compliance
  • Log verification failures: Monitor for potential attacks
  • Rate limit webhook endpoints: Protect against DoS attacks
  • Document for customers: Provide clear verification instructions

Next Steps