In this article, by Tim Rogers author of Twilio Best Practices, we'll see how to keep our Twilio account and applications (and ultimately credit) secure by:
(For more resources related to this topic, see here.)
Twilio offers two-factor authentication functionality that we can enable on our account.
This will give you much greater security if someone tries to break into your account following the something you know and something you have model. Apart from your password, Twilio will send you an SMS or call you when a login attempt is made, requiring you to enter a one-time password.
Not only will this largely prevent malicious access to your account, but you'll also know that someone is attempting to access your account, and what's more, that they have your password.
It's worth noting that, unsurprisingly, you can quite easily roll two-factor authentication functionality for your own application using Twilio's call and SMS functionality. Check out https://www.twilio.com/docs/howto/two-factor-authentication for help with getting started.
There are two steps to enable two-factor authentication:
Once you've added and verified your phone number on your user profile, you'll need to set up two-factor authentication on your account(s). To get to the right place, click on Account Settings in the dropdown.
If your login has been given access to another user's Twilio account, (that is, the account you access from the Switch Accounts menu option) an administrator on that account will need to repeat this process.
From this page, you'll be able to choose between two different two-factor options (you can also disable the feature here):
Once you're done, click on the Save Settings button at the bottom of your page to set up the two-factor authentication.
There are a couple of other features you might want to check out on the Account Settings page.
You can reset your API credentials if you accidentally reveal them and you can disable parts of Twilio's Request Inspector which might potentially store sensitive information from your application and require passwords in order to access recordings and media you send in MMS messages.
If parties other than Twilio are able to make requests to your application, they can potentially change and corrupt data or access sensitive information.
Without authentication measures, if an attacker was able to guess the URLs of the endpoints on your application that Twilio hits with its webhooks, they could wreak havoc. For instance, they could spoof fake SMS messages so that they appear to come from users or they could access the private phones numbers of users they should only be able to call through a public line you provide.
There are two routes you can take to prevent this, ensuring with a reasonable degree of certainty that a request genuinely comes from Twilio:
HTTP Basic Authentication simply allows you to require a username and password to access your web server's resources.
If you're working with PHP, you'll want to set this up on the web server level. This is possible in most servers, including:
If you're not using one of these, you can be virtually certain anyway that this option will be available to you; simply have a look at its documentation or search the web.
Alternatively, you can implement Basic Authentication in your PHP code using code along these lines. We'll store the username and password in environment variables for security:
<?php if (!isset($_SERVER['PHP_AUTH_USER'])) { // The user didn't even try to authenticate, so sent 401 Unauthorized header('WWW-Authenticate: Basic realm="Twilio only!"'); header('HTTP/1.0 401 Unauthorized'); exit; } elseif ($_SERVER['PHP_AUTH_USER'] == $_ENV["TWILIO_USERNAME"] && $_SERVER['PHP_AUTH_PW'] == $_ENV["TWILIO_PASSWORD"]) { // The user authenticated successfully, so perform actions and output TwiML } else { // The user tried to authenticate, but didn't have the right credentials header('WWW-Authenticate: Basic realm="Twilio only!"'); header('HTTP/1.0 401 Unauthorized'); exit; } ?>
Let's go through this bit by bit:
When we're providing a URL to Twilio (for instance, when initiating a call via the REST API, or setting it for incoming calls or SMS messages from our Dashboard), we can set the username and password in this format:
Alternatively, instead of using a username and password, we can verify the cryptographic signature Twilio generates with its requests based upon our auth token which is sent in the X-Twilio-Signature header.
The scheme for doing this is somewhat complicated (you can find it in full at https://www.twilio.com/docs/security#validating-requests) but fortunately, Twilio provides validation functionality in their API libraries alongside code samples.
For this method of verification to be available, you'll need to serve your application over HTTPS with Transport Layer Security (TLS) enabled. In fact, you should always do this with your Twilio application, as a good security practice.
Following the SSLv3 vulnerability discovered in October, 2014 known as POODLE, you'll want to double-check the security of any SSL configuration. See https://www.digitalocean.com/community/tutorials/how-to-protect-your-server-against-the-poodle-sslv3-vulnerability for details.
In PHP, we'd execute the following:
<?php // Load auth token from the TWILIO_AUTH_TOKEN environment variable $authToken = $_ENV['TWILIO_AUTH_TOKEN']; // You'll need to make sure the Twilio library is included, either by requiring // it manually or loading Composer's autoload.php $validator = new Services_Twilio_RequestValidator($authToken); $url = $_SERVER["SCRIPT_URI"]; $vars = $_GET; $signature = $_SERVER["HTTP_X_TWILIO_SIGNATURE"]; if ($validator->validate($signature, $url, $vars)) { // This request definitely came from Twilio, so continue onwards... } else { // Watch out - this is not a real request from Twilio. header('HTTP/1.0 401 Unauthorized'); } ?>
Here, we instantiate a Services_Twilio_RequestValidator object from the API library with our auth token before passing in the requested URL, the request body ($_GET in this case, but for a POST request, this would be $_POST), and the signature.
We then call the validator's validate method with these pieces of data, allowing it to generate the signature itself, and comparing it against what we received in the X-Twilio-Signature header. If it matches, the request is genuine, but if not, the request is spoofed and is not from Twilio.
Using Twilio's Usage Triggers allows us to build a circuit breaker.
In short, this will let us know when one of our subaccounts passes certain amounts of usage, which will help us detect possible abuse of our account, as well as mistakes in our code. It can even help detect abuse if we were running a multitenant app (that is, offering Twilio-based services to our users).
When our specified usage threshold is surpassed, Twilio will send a webhook to a URL of our choice. From this URL, we can perform a range of actions, whether that is sending ourselves an e-mail or even suspending the account in question.
Here, we'll just run through a quick example of suspending an account if it spends more than $50 in one day.
We'll set up our Usage Trigger using the Twilio dashboard. To do this, first log in, and then switch to the appropriate subaccount you'd like to set up the trigger for by clicking on your name in the top-right corner. Next, click on Subaccounts, and then click on the desired account.
Next, click on Usage in the navigation bar, then click on Triggers underneath, and then click on the Create Usage Trigger button.
Fill out the fields as shown in the following image. First, you'll need to click on the Trigger a webhook link on the right-hand side of the page where Send an email appears in the screenshot to set up a webhook, replacing the URL with one that would be accessible from a domain of your own, of course.
We might also want to automate this process of setting up a usage trigger using the REST API. For example, we might want to automatically suspend a subaccount we've just created for a customer if their usage goes beyond reasonable limits. In order to do so, we'd do the same thing we did previously in PHP like this:
<?php $accountSid = $_ENV['TWILIO_ACCOUNT_SID']; $authToken = $_ENV['TWILIO_AUTH_TOKEN']; $subaccountSid = '<the SID of the subaccount>'; // You'll need to make sure the Twilio library is included, either by requiring // it manually or loading Composer's autoload.php $client = new Services_Twilio($accountSid, $authToken); $account = $client->accounts->get($subaccountSid); $account->usage_triggers->create( 'totalprice', '+50', 'https://twiliobestpractices.com/path/to/trigger_action.php', array( 'Recurring' => 'daily', 'TriggerBy' => 'price', 'FriendlyName' => 'Suspend if uses more than $50 per day' ) ); ?>
Both options do exactly the same thing. They create a trigger that will send a webhook to when more than $50 is spent by our subaccount on any one day.
It's completely up to us what we do from the endpoint that receives the webhook. Anything we can program is possible, from suspending the account to calling an engineer to look into it.
Here's some example code to go with the usage trigger we just set up that will automatically suspend the account once it goes over $50 spend in a day.
In this code sample, we also verify the authenticity of the request, as we saw previously, making sure that this is a genuine Usage Trigger webhook from Twilio:
<?php // Before starting, you'll need to require the Twilio PHP library // We'll load the SID of the subaccount that the trigger relates to and its // auth token from environment variables, but in reality, you're likely to be // loading them from a database of your users' details based on a passed-in ID, // or something along those lines. $subaccountSid = $_ENV['TWILIO_SUBACCOUNT_SID']; $subaccountAuthToken = $_ENV['TWILIO_SUBACCOUNT_AUTH_TOKEN']; $url = $_SERVER['SCRIPT_URI']; $signature = $_SERVER['HTTP_X_TWILIO_SIGNATURE']; $validator = new Services_Twilio_RequestValidator($subaccountAuthToken); if ($validator->validate($signature, $url, $_POST)) { $client = new Services_Twilio($subaccountSid, $subaccountAuthToken); $client->account->update(array('Status' => 'suspended')); } else { header('HTTP/1.0 401 Unauthorized'); } ?>
We've shown you examples of doing all of this using the PHP library, but you can do it with any of Twilio's libraries . You can also do this directly with the REST API using a tool such as Postman.
In this article, we saw three helpful tips to keep your Twilio account and application secure: