Skip to contentSkip to navigationSkip to topbar
Rate this page:
On this page

Security



Encrypted Communication

encrypted-communication page anchor

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(link takes you to an external page)

(warning)

Warning

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(link takes you to an external page).

(warning)

Warning

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.


_10
https://username:password@www.myserver.com/my_secure_document

(warning)

Warning

Twilio supports the TLS cryptographic protocol. Twilio cannot currently handle self-signed certificates, and support for SSLv3 is officially deprecated.

(warning)

Warning

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 (link takes you to an external page)Basic and (link takes you to an external page)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:


_10
HTTP/1.1 401 UNAUTHORIZED
_10
WWW-Authenticate: Basic realm="My Realm"
_10
Date: Wed, 21 Jun 2017 01:14:36 GMT
_10
Content-Type: application/xml
_10
Content-Length: 327


Protect Media Access with HTTP Basic Authentication

protect-media-access-with-http-authentication page anchor

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:

  • Programmable Messaging (ie. MMS, Whatsapp Facebook Business Messenger, Google Business, etc)
  • Programmable Voice (ie. call recordings)

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(link takes you to an external page)

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(link takes you to an external page).


Validating Requests are coming from Twilio

validating-requests page anchor

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:

  1. Turn on TLS on your server and configure your Twilio account to use HTTPS URLs.
  2. Twilio assembles its request to your application, including the final URL and any 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.
  3. Twilio takes the resulting string (the full URL with the scheme, port, query string and any POST parameters) and signs it using HMAC-SHA1 and your AuthToken as the key.
  4. Twilio sends this signature in an HTTP header called 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:

Validate Signature of Request

validate-signature-of-request page anchor
Node.js
Python
C#
Java
Go
PHP
Ruby

_26
// Get twilio-node from twilio.com/docs/libraries/node
_26
const client = require('twilio');
_26
_26
// Your Auth Token from twilio.com/console
_26
const authToken = process.env.TWILIO_AUTH_TOKEN;
_26
_26
// Store Twilio's request URL (the url of your webhook) as a variable
_26
const url = 'https://mycompany.com/myapp';
_26
_26
// Store the application/x-www-form-urlencoded parameters from Twilio's request as a variable
_26
// In practice, this MUST include all received parameters, not a
_26
// hardcoded list of parameters that you receive today. New parameters
_26
// may be added without notice.
_26
const params = {
_26
CallSid: 'CA1234567890ABCDE',
_26
Caller: '+12349013030',
_26
Digits: '1234',
_26
From: '+12349013030',
_26
To: '+18005551212',
_26
};
_26
_26
// Store the X-Twilio-Signature header attached to the request as a variable
_26
const twilioSignature = 'Np1nax6uFoY6qpfT5l9jWwJeit0=';
_26
_26
// Check if the incoming signature is valid for your application URL and the incoming parameters
_26
console.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.

(information)

Info

We highly recommend you use the helper libraries to do signature validation.

Explore the algorithm yourself

explore-the-algorithm-yourself page anchor

Here's how you would perform the validation on your end:

  1. Take the full URL of the request URL you specify for your phone number or app, from the protocol (https...) through the end of the query string (everything after the ?).
  2. If the request is a POST , sort all the POST parameters alphabetically (using Unix-style case-sensitive sorting order).
  3. Iterate through the sorted list of POST parameters, and append the variable name and value (with no delimiters) to the end of the URL string.
  4. Sign the resulting string with HMAC-SHA1 using your AuthToken as the key (remember, your AuthToken's case matters!).
  5. Base64 encodes the resulting hash value.
  6. Compare your hash to ours, submitted in the X-Twilio-Signature header. If they match, then you're good to go.

Let's walk through an example request. Let's say Twilio made a POST to your application as part of an incoming call webhook:


_10
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 : 1234
  • To : +18005551212
  • From : +14158675310
  • Caller : +14158675310
  • CallSid : CA1234567890ABCDE

Create a string that is your URL with the full query string:


_10
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 : CA1234567890ABCDE
  • Caller : +14158675310
  • Digits : 1234
  • From : +14158675310
  • To : +18005551212

Next, append each POST variable, name and value, to the string with no delimiters:


_10
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):


_10
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):


_10
GvWf1cFY/Q7PnoempGyD5oXAezc=

Finally, compare that to the hash Twilio sent in the X-Twilio-Signature HTTP header. If they match, the request is valid!

(warning)

Warning

This example is for illustrative purposes only. When validating requests in your application, only use the provided helper methods.

  • If the Content-Type is application-json, don't use the JSON body to fill in the validator's param for POST parameters.
    • The query parameter bodySHA256 will be included in the request.
    • Its value is calculated as the hexadecimal representation of the SHA-256 hash of the request body.
  • Some frameworks may trim whitespace from 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.
    • When manually constructing the request body to be sent (as can be done in the Studio HTTP Request widget) ensure that no hidden whitespaces are in the body.
  • When creating the hash make sure you are using your Primary AuthToken as the key. If you have recently created a secondary AuthToken, this means you still need to use your old AuthToken until the secondary one has been promoted to your primary AuthToken(link takes you to an external page) .
  • The HMAC-SHA1 secure hashing algorithm should be available in all major languages, either in the core or via an extension or package.
  • If your URL uses an "index" page, such as 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/ .
  • For SMS and voice callbacks over HTTP:
    • Twilio will drop the username and password (if any) from the URL before computing the signature.
    • Twilio will keep the port (if any) in the URL when computing the signature.
  • For SMS callbacks over HTTPS:
    • Twilio will drop the username and password (if any) from the URL before computing the signature.
    • Twilio will keep the port (if any) in the URL when computing the signature.
  • For voice callbacks over HTTPS:
    • Twilio will drop the username and password (if any) from the URL before computing the signature.
    • Twilio will also drop the port (if any) from the URL before computing the signature.

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.

(information)

A note on HMAC-SHA1

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(link takes you to an external page), HMACs(link takes you to an external page) 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.


Test the validity of your webhook signature

test-the-validity-of-your-webhook-signature page anchor
(information)

Info

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:

  1. Set your Auth Token(link takes you to an external page) as an environment variable
  2. Set the URL to the endpoint you want to test
  3. If testing BasicAuth, change HTTPDigestAuth to HTTPBasicAuth

Test the validity of your webhook signature

test-the-validity-of-your-webhook-signature-1 page anchor
Node.js
Python
C#
Java
PHP
Ruby

_46
// Get twilio-node from twilio.com/docs/libraries/node
_46
const webhooks = require('twilio/lib/webhooks/webhooks');
_46
const request = require('request');
_46
_46
// Your Auth Token from twilio.com/console
_46
const authToken = process.env.TWILIO_AUTH_TOKEN;
_46
_46
// The Twilio request URL
_46
const url = 'https://mycompany.com/myapp';
_46
_46
// The post variables in Twilio's request
_46
const params = {
_46
CallSid: 'CA1234567890ABCDE',
_46
Caller: '+12349013030',
_46
Digits: '1234',
_46
From: '+12349013030',
_46
To: '+18005551212',
_46
};
_46
_46
_46
function testUrl(method, url, params, valid) {
_46
if(method === "GET") {
_46
url += "?" + Object.keys(params).map(key => key + '=' + params[key]).join('&');
_46
params = {};
_46
}
_46
const signatureUrl = valid ? url : "http://invalid.com";
_46
const signature = webhooks.getExpectedTwilioSignature(authToken, signatureUrl, params);
_46
const options = {
_46
method: method,
_46
url: url,
_46
form: params,
_46
headers: {
_46
'X-Twilio-Signature': signature
_46
}
_46
}
_46
_46
request(options, function(error, response, body){
_46
const validStr = valid ? "valid" : "invalid";
_46
console.log(`HTTP ${method} with ${validStr} signature returned ${response.statusCode}`);
_46
});
_46
}
_46
_46
testUrl('GET', url, params, true);
_46
testUrl('GET', url, params, false);
_46
testUrl('POST', url, params, true);
_46
testUrl('POST', url, params, false);

Validation using the Twilio Helper Libraries

validation-with-libraries page anchor

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.

Your Auth Token

auth-token page anchor

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.


Rate this page: