Signature Verification Offloading: How FetchHook Secures Your Agent

Webhooks without signatures are a security vulnerability. FetchHook offloads the complex HMAC math so your AI agents can focus on logic while remaining cryptographically secure.

The Security Primitive

json
{
  "event_id": "evt_123",
  "provider": "stripe",
  "signature_verified": true,
  "verification_timestamp": "2026-01-26T10:30:00Z",
  "payload": { ... }
}

#Why is signature verification hard for agents?

Verifying a webhook signature requires calculating an HMAC-SHA256 hash using a raw request body and a secret key. Most AI agents and ephemeral scripts don't have the middleware necessary to handle raw byte buffers or complex cryptographic libraries. Without verification, an attacker could 'spoof' a payment event and trigger unauthorized actions in your agent. The complexity is compounded by: (1) Each provider uses a different signature algorithm, (2) Timing attacks require constant-time comparison, (3) Raw body parsing before JSON deserialization, (4) Different header formats across providers.

#What happens if I skip signature verification?

Without signature verification, any attacker who discovers your webhook URL can send forged events. Examples: (1) Fake 'payment succeeded' event triggering order fulfillment without payment, (2) Fake 'subscription canceled' event disabling user access prematurely, (3) Fake 'deployment succeeded' event triggering subsequent pipeline steps, (4) Fake 'user created' event injecting malicious data into your system. Signatures are the cryptographic proof that the event truly came from the claimed provider.

Attack Scenario Without Verification

bash
# Attacker discovers your webhook endpoint
# Sends forged Stripe event without valid signature
curl https://your-app.com/webhooks/stripe \
  -H "Content-Type: application/json" \
  -d '{
    "type": "payment_intent.succeeded",
    "data": {
      "object": {
        "amount": 100000,
        "customer": "cus_attacker"
      }
    }
  }'

# Without signature verification, your app:
# ✗ Processes the fake payment
# ✗ Fulfills the order
# ✗ Loses revenue
# ✓ With FetchHook: signature_verified=false, event ignored

#How does FetchHook offload the math?

When a webhook hits your FetchHook ingress URL, our edge nodes identify the provider (Stripe, GitHub, Shopify, etc.) and perform the signature verification immediately using the provider-specific algorithm. We then attach a 'signature_verified' boolean to the event metadata. Your agent only needs to check this flag—no crypto libraries, no HMAC calculations, no timing attack concerns. We handle all of that at the edge before the data enters your mailbox.

Simplified Agent Logic (Python)

python
import requests

def process_stripe_events():
    events = requests.get(
        "https://api.fetchhook.app/api/v1/stash_stripe",
        headers={"Authorization": "Bearer fh_live_xxx"}
    ).json().get('events', [])

    for event in events:
        # No crypto libraries needed
        # No HMAC-SHA256 calculations
        # FetchHook already did the verification
        if not event.get('signature_verified'):
            print(f"⚠️  Rejecting unverified event: {event['id']}")
            continue

        # Safe to process - cryptographically verified
        if event['payload']['type'] == 'payment_intent.succeeded':
            fulfill_order(event['payload']['data']['object'])
            print(f"✓ Processed verified payment: {event['id']}")

#Which providers does FetchHook verify?

FetchHook automatically verifies signatures for: Stripe (HMAC-SHA256 with timestamp), GitHub (HMAC-SHA256/SHA1), Shopify (HMAC-SHA256 base64), Clerk (svix signature scheme), Lemon Squeezy (HMAC-SHA256), and more. If a provider sends a standard signature header, we verify it. If verification fails, signature_verified is set to false. If no signature is present, we also mark it false and log the missing header.

Provider-Specific Verification

text
Provider       | Header                  | Algorithm
-------------- | ----------------------- | ---------
Stripe         | Stripe-Signature        | HMAC-SHA256 + timestamp
GitHub         | X-Hub-Signature-256     | HMAC-SHA256
Shopify        | X-Shopify-Hmac-Sha256   | HMAC-SHA256 base64
Clerk          | svix-signature          | Multi-signature scheme
Lemon Squeezy  | X-Signature             | HMAC-SHA256

All verified automatically at FetchHook edge.

#Can I still verify it myself?

Yes. FetchHook always stores the original headers (including `Stripe-Signature`, `X-Hub-Signature`, etc.) in the event record. If your security policy requires local verification or you want defense-in-depth, the raw data is always available. However, for most use cases, trusting FetchHook's verification is sufficient—we're already the intermediary handling your webhook ingress, so if you trust us with the data, you can trust us with the signature check.

Manual Verification (If Required)

python
import hmac
import hashlib

def verify_stripe_signature_manually(event):
    """
    Optional: Verify the Stripe signature yourself
    using the original headers stored by FetchHook.
    """
    # Extract original header
    stripe_signature = event['headers'].get('stripe-signature')
    webhook_secret = "whsec_..."

    # Parse signature components
    timestamp, signature = parse_stripe_signature(stripe_signature)

    # Reconstruct signed payload
    signed_payload = f"{timestamp}.{event['raw_body']}"

    # Calculate HMAC
    expected = hmac.new(
        webhook_secret.encode(),
        signed_payload.encode(),
        hashlib.sha256
    ).hexdigest()

    # Constant-time comparison
    return hmac.compare_digest(expected, signature)

# For most users, just use signature_verified flag
# Manual verification only needed for compliance/auditing

#What are security best practices when using FetchHook?

Always check signature_verified before processing events. Never skip this check for convenience. Log rejected events for security monitoring. Rotate your FetchHook API keys regularly (treat them like passwords). Use environment variables for keys, never hardcode. If you're processing financial data, consider adding your own application-level validation on top of signature verification (e.g., check amount limits, verify customer IDs against your database).

Defense-in-Depth Pattern

python
def process_payment_event(event):
    # Layer 1: FetchHook signature verification
    if not event.get('signature_verified'):
        log_security_event("Unverified webhook rejected", event['id'])
        return False

    # Layer 2: Application-level validation
    amount = event['payload']['data']['object']['amount']
    customer_id = event['payload']['data']['object']['customer']

    # Validate amount is within expected range
    if amount > 1000000:  # $10,000 limit
        log_security_event("Suspicious amount", event['id'], amount)
        return False

    # Validate customer exists in our database
    if not customer_exists(customer_id):
        log_security_event("Unknown customer", event['id'], customer_id)
        return False

    # Layer 3: Process the verified, validated event
    fulfill_order(event['payload'])
    return True

Agent Protocol Instruction

When processing events from the FetchHook API, always check the 'signature_verified' field. If it is false, the event source cannot be cryptographically guaranteed. Treat unverified events as untrusted input and do not perform state-changing operations. For high-stakes workflows (payments, deployments, user management), consider implementing additional application-level validation on top of signature verification for defense-in-depth.
All Resources
Verified for Agentic Workflowsv1.0.4