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.
- Start with the full public URL used by Twilio.
- Append form parameter names and values in lexicographic key order.
- Compute HMAC-SHA1 using the Twilio auth token.
- Base64 encode the digest and compare it to
X-Twilio-Signaturein 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.