Webhook security

Verify Twilio callbacks before trusting them.

Inbound voice webhooks must preserve the exact public URL and form parameters used by Twilio, then compare the HMAC-SHA1 signature in constant time.

Twilio signature algorithm

Twilio computes X-Twilio-Signature from the exact public request URL, the submitted form parameters, and the tenant Twilio auth token. Preserve the original URL and form body before verification.

  1. Start with the full public URL used by Twilio.
  2. Append form parameter names and values in lexicographic key order.
  3. Compute HMAC-SHA1 using the Twilio auth token.
  4. Base64 encode the digest and compare it to X-Twilio-Signature in constant time.

Node.js verification

import crypto from "node:crypto";

function verifyTwilioSignature(url, params, signature, authToken) {
  const data = Object.keys(params)
    .sort()
    .reduce((acc, key) => acc + key + params[key], url);

  const expected = crypto
    .createHmac("sha1", authToken)
    .update(Buffer.from(data, "utf-8"))
    .digest("base64");

  const left = Buffer.from(signature);
  const right = Buffer.from(expected);
  return left.length === right.length && crypto.timingSafeEqual(left, right);
}

Python verification

import base64
import hashlib
import hmac

def verify_twilio_signature(url, params, signature, auth_token):
    data = url + "".join(key + params[key] for key in sorted(params))
    digest = hmac.new(
        auth_token.encode("utf-8"),
        data.encode("utf-8"),
        hashlib.sha1,
    ).digest()
    expected = base64.b64encode(digest).decode("utf-8")
    return hmac.compare_digest(signature, expected)

Deployment checks

  • Verify the public URL behind your load balancer is the same URL Twilio signs.
  • Reject missing signatures in production.
  • Rotate auth tokens with a previous-token overlap window and clear audit logs when rotation completes.