Twilio supports encryption to protect communications between Twilio and your web application. Just specify an HTTPS URL. Twilio will not connect to an HTTPS URL with a self-signed certificate, so use a certificate from a provider such as Let's Encrypt
Be aware Twilio strongly recommends against pinning certificates. This is an outdated practice as certificates can be rotated at any time.
Twilio can use the HTTP protocol for callbacks - for instance, if you are working on a development environment that does not have SSL certificates installed. On your Twilio project's Settings page in the Console, the SSL Certificate Validation setting enforces validation on webhooks.
Here is the list of supported TLS ciphers for callbacks.
Twilio supports the TLS cryptographic protocol. Twilio cannot currently handle self-signed certificates, and support for SSLv3 is officially deprecated.
Twilio supports HTTP Basic and Digest Authentication. This allows you to password-protect the TwiML URLs on your web server so that only you and Twilio can access them. You may provide a username and password via the following URL format.
https://username:password@www.myserver.com/my_secure_document
Twilio supports the TLS cryptographic protocol. Twilio cannot currently handle self-signed certificates, and support for SSLv3 is officially deprecated.
Be careful to not include any special characters, such as &,``:
, etc., in your username or password.
Twilio will authenticate to your web server using the provided username and password and will remain logged in for the duration of the call. We highly recommend that you use HTTP Authentication in conjunction with encryption. For more information on Basic and Digest Authentication, refer to your web server documentation.
If you specify a password-protected URL, Twilio will first send a request with
no Authorization
header. After your server responds with a 401 Unauthorized
status code, a WWW-Authenticate
header and a realm
in the response, Twilio will make the same request with an Authorization
header.
Example response from your server:
1HTTP/1.1 401 UNAUTHORIZED2WWW-Authenticate: Basic realm="My Realm"3Date: Wed, 21 Jun 2017 01:14:36 GMT4Content-Type: application/xml5Content-Length: 327
Media files, such as call recordings in Programmable Voice; or an image associated with any Programmable Messaging channel (eg. MMS, WhatsApp, or Facebook), can be stored in our Services.
Requiring HTTP Basic Authentication for stored media is considered industry best practice, and it is implemented by Twilio for all applicable Services. Some of our products such as Programmable Voice and Programmable Messaging, support HTTP Basic Authentication but aren't enabled by default. It is an opt-in setting that must be enabled for your applicable Twilio Account and sub-accounts.
To protect media access, you can enforce authentication to access them by enabling HTTP Basic Authentication in your Twilio Account. This setting requires your Twilio Account SID and Auth Token or API Key for all requests to access media files.
Twilio highly recommends enabling HTTP Basic Authentication for your media, especially if it contains Sensitive Data.
Please note, that you'll need to manually enable HTTP Basic Authentication for media access in the following Services and functionalities:
The first step you should take to secure your web application is to ensure that you are using HTTPS for your web application's endpoint. Twilio will not connect to an HTTPS URL with a self-signed certificate, so use a certificate from a provider such as Let's Encrypt
Twilio can use the HTTP protocol for callbacks - for instance, if you are working on a development environment that does not have SSL certificates installed. On your Twilio project's Settings page in the Console, the SSL Certificate Validation setting enforces validation on webhooks.
Here is the list of supported TLS ciphers for callbacks.
If your application exposes sensitive data or is possibly mutative to your data, then you may want to be sure that the HTTP requests to your web application are indeed coming from Twilio, and not a malicious third party. To allow you this level of security, Twilio cryptographically signs its requests. Here's how it works:
POST
fields.
* If your request is a POST
, Twilio takes all the POST
fields, sorts them alphabetically by their name, and concatenates the parameter name and value to the end of the URL (with no delimiter). Only query parameters get parsed to generate a security token, not the POST
body.
* If the request is a GET
, the final URL includes all of Twilio's request parameters appended in the query string of your original URL using the standard delimiter &
between the name/value pairs.POST
parameters) and signs it using HMAC-SHA1 and your AuthToken as the key.X-Twilio-Signature
Then, on your end, if you want to verify the authenticity of the request, you can leverage the built-in request validation method provided by all of our helper libraries:
1// Get twilio-node from twilio.com/docs/libraries/node2const client = require('twilio');34// Your Auth Token from twilio.com/console5const authToken = process.env.TWILIO_AUTH_TOKEN;67// Store Twilio's request URL (the url of your webhook) as a variable8const url = 'https://mycompany.com/myapp';910// Store the application/x-www-form-urlencoded parameters from Twilio's request as a variable11// In practice, this MUST include all received parameters, not a12// hardcoded list of parameters that you receive today. New parameters13// may be added without notice.14const params = {15CallSid: 'CA1234567890ABCDE',16Caller: '+12349013030',17Digits: '1234',18From: '+12349013030',19To: '+18005551212',20};2122// Store the X-Twilio-Signature header attached to the request as a variable23const twilioSignature = 'Np1nax6uFoY6qpfT5l9jWwJeit0=';2425// Check if the incoming signature is valid for your application URL and the incoming parameters26console.log(client.validateRequest(authToken, twilioSignature, url, params));
If the method call returns true
, then the request can be considered valid and it is safe to proceed with your application logic.
We highly recommend you use the helper libraries to do signature validation.
Here's how you would perform the validation on your end:
POST
, sort all the POST
parameters alphabetically (using Unix-style case-sensitive sorting order).POST
parameters, and append the variable name and value (with no delimiters) to the end of the URL string.Let's walk through an example request. Let's say Twilio made a POST
to your application as part of an incoming call webhook:
https://mycompany.com/myapp.php?foo=1&bar=2
And let's say Twilio posted some digits from a Gather
to that URL, in addition to all the usual POST
fields:
Digits
: 1234To
: +18005551212From
: +14158675310Caller
: +14158675310CallSid
: CA1234567890ABCDECreate a string that is your URL with the full query string:
https://mycompany.com/myapp.php?foo=1&bar=2
Then, sort the list of POST
variables by the parameter name (using Unix-style case-sensitive sorting order):
CallSid
: CA1234567890ABCDECaller
: +14158675310Digits
: 1234From
: +14158675310To
: +18005551212Next, append each POST
variable, name and value, to the string with no delimiters:
https://mycompany.com/myapp.php?foo=1&bar=2CallSidCA1234567890ABCDECaller+14158675310Digits1234From+14158675310To+18005551212
Hash the resulting string using HMAC-SHA1, using your AuthToken Primary as the key.
Let's suppose your AuthToken is 12345. Then take the hash value returned from the following function call (or its equivalent in your language of choice):
hmac_sha1(https://mycompany.com/myapp.php?foo=1&bar=2CallSidCA1234567890ABCDECaller+14158675310Digits1234From+14158675310To+18005551212, 12345)
Now take the Base64 encoding of the hash value (so it's only ASCII characters):
GvWf1cFY/Q7PnoempGyD5oXAezc=
Finally, compare that to the hash Twilio sent in the X-Twilio-Signature HTTP header. If they match, the request is valid!
This example is for illustrative purposes only. When validating requests in your application, only use the provided helper methods.
Content-Type
is application-json
, don't use the JSON body to fill in the validator
's param for POST
parameters.
bodySHA256
will be included in the request.POST
body fields. A notable example is Laravel, which has the TrimStrings middleware enabled by default. You must disable these behaviors to successfully match signatures generated from fields that have leading or trailing whitespace. Certain Node.js middleware may also trim whitespace from requests.
index.php
or index.html
to handle the request, such as: https://mycompany.com/twilio
where the real page is served from https://mycompany.com/twilio/index.php
, then Apache or PHP may rewrite that URL so it has a trailing slash, e.g., https://mycompany.com/twilio/
. Using the code above, or similar code in another language, you could end up with an incorrect hash, because Twilio built the hash using https://mycompany.com/twilio
and you may have built the hash using https://mycompany.com/twilio/
./
character to the URL that you pass to the signature validation method.This behavior will continue to be supported in the 2008-08-01 and 2010-04-01 versions of the API to ensure compatibility with existing code. We understand this behavior is inconsistent, and apologize for the inconvenience.
Concerned about SHA1 security issues? Twilio does not use SHA-1 alone.
In short, the critical component of HMAC-SHA1
that distinguishes it from SHA-1
alone is the use of your Twilio AuthToken as a complex secret key. While there are possible collision-based attacks on SHA-1, HMACs are not affected by those same attacks - it's the combination of the underlying hashing algorithm (SHA-1) and the strength of the secret key (AuthToken) that protects you in this case.
It's a great idea to test your webhooks and ensure that their signatures are secure. The following sample code can test your unique endpoint against both valid and invalid signatures.
To make this test work for you, you'll need to:
HTTPDigestAuth
to HTTPBasicAuth
1// Get twilio-node from twilio.com/docs/libraries/node2const webhooks = require('twilio/lib/webhooks/webhooks');3const request = require('request');45// Your Auth Token from twilio.com/console6const authToken = process.env.TWILIO_AUTH_TOKEN;78// The Twilio request URL9const url = 'https://mycompany.com/myapp';1011// The post variables in Twilio's request12const params = {13CallSid: 'CA1234567890ABCDE',14Caller: '+12349013030',15Digits: '1234',16From: '+12349013030',17To: '+18005551212',18};192021function testUrl(method, url, params, valid) {22if(method === "GET") {23url += "?" + Object.keys(params).map(key => key + '=' + params[key]).join('&');24params = {};25}26const signatureUrl = valid ? url : "http://invalid.com";27const signature = webhooks.getExpectedTwilioSignature(authToken, signatureUrl, params);28const options = {29method: method,30url: url,31form: params,32headers: {33'X-Twilio-Signature': signature34}35}3637request(options, function(error, response, body){38const validStr = valid ? "valid" : "invalid";39console.log(`HTTP ${method} with ${validStr} signature returned ${response.statusCode}`);40});41}4243testUrl('GET', url, params, true);44testUrl('GET', url, params, false);45testUrl('POST', url, params, true);46testUrl('POST', url, params, false);
All the official Twilio Helper Libraries ship with a Utilities class which facilitates request validation. Head over to the libraries page to download the library for your language of choice.
Please keep your AuthToken secure. It not only enables access to the REST API but also to request signatures. Learn how to secure this token using environment variables.