IntegrationsAdvanced

How to Verify Webhook Signatures Before Calling an AI Model

Stop strangers from running up your AI bill by checking an HMAC signature in n8n or Make before any model call.

9 minAdvanced

A public webhook URL is open to anyone who finds it. If that URL triggers a paid AI model, an attacker can drain your credits with a loop of requests. The fix is a shared secret: the sender signs each payload, and your workflow rejects anything whose signature does not match before spending a token.

What you need

  • A webhook in n8n, Make, or Zapier that can read raw body and headers
  • A shared secret string you control on both ends
  • The ability to add a Code or Tools step before the AI call

Step 1: Sign the payload on the sender

Whatever sends the webhook should compute an HMAC SHA-256 of the exact request body using the shared secret, then put the hex digest in a header such as X-Signature.

sender.sh - sign and POST
SECRET="super-secret-value"
BODY='{"prompt":"hello"}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | sed 's/^.* //')

curl -X POST https://your-n8n.example.com/webhook/ai \
  -H "Content-Type: application/json" \
  -H "X-Signature: $SIG" \
  -d "$BODY"

Step 2: Recompute the signature inside the workflow

In n8n, add a Code node right after the Webhook node. Hash the raw body with the same secret and compare it to the header. Throw an error when they differ so the workflow stops before the model node runs.

n8n Code node - verify
const crypto = require('crypto');
const secret = 'super-secret-value';
const raw = JSON.stringify($input.first().json.body);
const sent = $input.first().json.headers['x-signature'];
const calc = crypto.createHmac('sha256', secret).update(raw).digest('hex');

if (!sent || calc !== sent) {
  throw new Error('Invalid signature - rejecting request');
}
return $input.all();
n8n - execution after a bad signature
Webhook ............. ok
Verify (Code) ............. ERROR
Invalid signature - rejecting request
AI Agent ............. not executed
Respond ............. not executed
The Code node halts the run before the AI node ever fires.
Match the bytes exactly
HMAC is computed over the raw body, character for character. If your platform re-serializes JSON (changing key order or spacing) the hashes will not match. When possible, sign and verify over the untouched raw string.

Step 3: Test both paths

bash - good vs forged request
valid signature
$bash sender.sh
{"answer":"..."}
forged header
$curl ... -H "X-Signature: deadbeef" -d '{"prompt":"hi"}'
{"message":"Invalid signature - rejecting request"}
$

Result: only signed requests reach the model, so a leaked URL alone cannot burn your AI budget.

Watch related tutorials

Tags
#webhooks#security#hmac#n8n