Webhook Security & Signature Verification

Provider-specific webhook signature verification, rate limiting, payload size controls, and replay attack prevention for payment processor integrations.

3 min read Security & Infrastructure As of Feb 9, 2026

Overview

Payment processor webhooks are the primary mechanism for asynchronous event delivery -- charge confirmations, payout status updates, refund completions, and dispute notifications. Because these endpoints are publicly accessible and directly affect financial records, CommunityPay applies layered security controls at every webhook entry point.


Rate Limiting

The WebhookRateLimitMiddleware applies per-provider, per-IP rate limits to all webhook endpoints:

Provider Rate Limit Window
Stripe 100 requests/minute 60 seconds
Dwolla 60 requests/minute 60 seconds
Plaid 50 requests/minute 60 seconds
Unknown 30 requests/minute 60 seconds

The middleware: 1. Detects webhook endpoints by URL path matching (/webhook in path) 2. Extracts the provider name from the URL structure 3. Constructs a per-provider, per-IP cache key 4. Uses Redis-backed sliding window counters 5. Returns HTTP 429 (Too Many Requests) when limits are exceeded

Rate limit violations are logged with the offending IP address for security monitoring.


Signature Verification

The WebhookSignatureVerifier implements provider-specific signature verification:

Stripe Verification

Stripe webhooks use a composite signature header (Stripe-Signature) containing a timestamp and one or more HMAC-SHA256 signatures:

  1. Parse the signature header to extract timestamp (t=) and signature values (v1=)
  2. Validate timestamp: Reject if the webhook is older than 300 seconds (5-minute replay window)
  3. Compute expected signature: HMAC-SHA256 of {timestamp}.{payload} using the webhook signing secret
  4. Compare: Use hmac.compare_digest for constant-time comparison, preventing timing-based attacks

Dwolla Verification

Dwolla uses a simpler HMAC-SHA256 signature: 1. Compute HMAC-SHA256 of the raw payload using the webhook secret 2. Compare against the X-Request-Signature-SHA-256 header using hmac.compare_digest

Plaid Verification

Plaid webhooks use JWT-based verification with the Plaid-Verification header.


Payload Size Limits

The @webhook_size_limit decorator enforces maximum payload sizes:

  • Default maximum: 512 KB
  • Checks the Content-Length header before processing
  • Returns HTTP 400 (Bad Request) for oversized payloads
  • Prevents memory exhaustion attacks on webhook endpoints

Replay Attack Prevention

The @verify_webhook_timestamp decorator provides an additional layer of replay protection:

  • Configurable maximum age (default: 300 seconds)
  • Rejects webhooks with timestamps outside the acceptable window
  • Works in conjunction with provider-specific timestamp validation

For Stripe specifically, timestamp validation is built into the signature verification itself -- the signed payload includes the timestamp, making it impossible to replay a webhook with a forged timestamp.


Decorator Composition

Webhook endpoints compose these security controls:

@require_webhook_signature('stripe')     # Verify signature present
@webhook_size_limit(max_size_kb=512)     # Enforce payload size
@verify_webhook_timestamp(max_age=300)   # Prevent replay
def stripe_webhook(request):
    # Process verified webhook
    ...

Each decorator operates independently, and any failure short-circuits the request before business logic executes.


Security Properties

Property Mechanism
Authentication HMAC-SHA256 signature per provider
Integrity Signature covers full payload
Freshness Timestamp validation (300s window)
Availability Per-provider rate limiting
Size bounds Payload size decorator
Timing safety hmac.compare_digest constant-time comparison
How CommunityPay Enforces This
  • Per-provider rate limits enforced via middleware (Stripe: 100/min, Dwolla: 60/min, Plaid: 50/min)
  • HMAC-SHA256 signature verification with constant-time comparison (hmac.compare_digest)
  • Replay attack prevention via 300-second timestamp validation window
  • Payload size limits enforced via decorator (default: 512 KB maximum)
Login