Protect Twilio Voice Input with Encryption and Redaction
Time to read: 6 minutes
Are you doing all you can to protect the sensitive information your callers trust you with?
As organizations leverage more sensitive information, securing that data is more important than ever. Twilio offers numerous ways you can protect your sensitive data—but it’s up to you to implement the resources Twilio provides responsibly.
In this article, learn how to encrypt and redact data collected from Twilio Programmable Voice, using <Gather> TwiML with Twilio Serverless Functions and Voice PCI Mode.
Things you'll need
In order to follow this tutorial, you will need:
What are you building?
You will build a simple interactive voice application to handle caller authentication. A Function will be used to prompt the caller for the following sensitive information via <Gather> TwiML.
- "Please enter your 4 digit PIN"
- "Please enter the last 4 digits of your payment card number"
As soon as this information is received from the caller, it will be encrypted. From that moment on, the data will remain encrypted until it reaches its destination.
In a real-world implementation, the destination would likely be your backend service for processing. But here, another Function will act as a “dummy API” to demonstrate how the decryption would be performed.
You will also enable Voice PCI Mode to redact gathered information in Voice call logs.
The Before
Before jumping into the solution, take a look at what your logs would look like without encryption or redaction.
Twilio Functions will log any error generated from a Function to your Twilio Debugger. In this example scenario, you will log an error if certain specific digits are not entered. You can see the plain-text request parameters in the error received by the Debugger.
Programmable Voice will also log the digits collected in plain-text in the Voice call log:
You can find this information if you have access to Call Logs or the Debugger.
The After
The data visible after implementing this solution is less vulnerable. By the end, your Function log will show more secure, encrypted values:
And your Call log will show *REDACTED*
:
Get Started
Twilio Functions
To follow along with these instructions, use the Twilio Console’s Function Editor.
Create a Service
Functions are created and contained within Services:
- Log in to the Twilio Console and navigate to the Functions tab.
- Create a Service by clicking the Create Service button and adding a name such as
encrypted-gather-sample
.
Add Dependency
In this solution, the axios
library is used to make a request to your “pretend” backend service (the decrypt-gather
Function) for processing.
Add axios
as a dependency to your Service.
Create an Environment Variable
This solution requires a secret key, which will be used to encrypt and decrypt the sensitive data.
Add an Environment Variable within your Service that stores your key.
For testing purposes, the following 32-byte secret key can be used.
Create AES
Encryption Function
First, create a Function to handle encryption and decryption of data using symmetric-key cryptography.
Node.js Crypto
Node.js offers a built-in cryptography module called Crypto. Crypto provides several useful methods, like createCipheriv()
and createDecipheriv()
which allow us to specify what kind of block-cipher algorithm to employ.
GCM Block Cipher
Advanced Encryption Standard, known as AES, is a technique for protecting data using encryption algorithms. AES can be achieved through a variety of modes of operations.
In this solution, you’ll be using GCM, Galois/Counter Mode, a symmetric-key cryptographic block cipher which is preferred for its speed and strength.
Code
Create a new Function called AES
with the following code.
This Function should be set to a visibility of "Private", as it will only be used from within another Function in the same Service.
Create encrypted-gather
Function
Next, create the Function that will perform the sensitive <Gather> operations. This Function will be configured as the incoming Phone Number voice webhook in a later step.
From this Function, the digits entered by the caller will be encrypted as soon as they are received, and sent in their encrypted state to the final, “destination” Function.
Code
Create a new Function called encrypted-gather
with the following code:
This Function should be set to "Protected", as it will be called from within Twilio and can be secured with the X-Twilio-Signature
header.
How is it encrypting?
At the top, you import the functions created in the previous step with the following line:
Then, you define your secret by getting it from the environment variable:
And, most importantly, any sensitive information is wrapped with the encrypt
function. (In this case, <Gather>'d information is passed as the Digit
parameter, and can be accessed from the event object.)
This handles the encryption of the gathered information.
Create decrypt-gather
Function
Finally, let’s create a Function to demonstrate how to decrypt the sensitive data.
In a production environment, this would likely be a request to your backend service that processes the caller information based on your business needs.
In this solution, a third Function will act as the “backend service” that processes this data. This Function will receive the encrypted digits and decrypt them for further processing.
Code
Create a new Function called decrypt-gather
with the following code:
This Function’s visibility will be "Public", as it is pretending to be an external service.
How is it decrypting?
At the top, you import AES
functions again and define the secret_key
as a variable.
Then you call decrypt
on the information that was previously encrypted:
Additional Configuration
Phone Number Webhook
For the sake of simplicity, connect this Function directly to a Phone Number.
To configure the Phone Number:
- From the Twilio Console, navigate to the Phone Numbers section
- Select your phone number, then scroll to the Voice & Fax section
- Set the
encrypted-gather
Function as the A call comes in webhook under Voice Configuration - Save changes
Enable PCI Mode
Almost done! You’ve secured the Functions, but there’s still one more area where Twilio retains gathered digits in plain-text – Voice call logs.
Below is a screenshot from the Twilio Console for an inbound call with the encrypted <Gather> solution implemented. Even though Functions secured the data, Voice hasn’t.
There’s only one way to prevent this data from being displayed in the Call log, and that’s with PCI Mode. Enabling PCI Mode on your account will redact all data captured from any <Gather> operation.
If you’re serious about capturing sensitive information securely…
- Navigate to the Twilio Voice Settings in the Twilio Console. (In the left navigation pane, click on Voice > Settings > General.)
- Click on the Enable PCI Mode button.
- Save changes.
Make a call
Now it’s the moment of truth—it’s time to place a test call to the phone number.
From here, there are two paths to take.
If you enter 1234
as the last 4 digits of your “credit card” and 4321
as the unique PIN, you’ll hear some “dummy” account information returned on the call. This is an example of a successful API response.
If you enter any other digits, it will behave as though you aren’t a known user and return a 404
response. This is an example of an unsuccessful request, which will log an error to the Twilio Debugger.
How do I know it worked?
Follow the unsuccessful path and take a look at your Error log in the Twilio Console.
For the 404
error response, you’ll find an 82005 Error from Functions with the following details:
This is good. Without the encryption, an unsuccessful response would have logged those variables in plain-text. But now the data will log in its safer, encrypted form.
You can also check your Call log to confirm the digits show *REDACTED*
there as well.
Is this secure?
Following this tutorial (including the optional PCI Mode steps) would prevent the data from logging in plain-text anywhere within Twilio’s ecosystem, and it would prevent anyone at Twilio from being able to decrypt your sensitive data – making this an improvement over the default.
However, the secret key used for encryption and decryption is stored as an Environment Variable on the Service, meaning users to whom you grant Twilio Functions access would be able to extract the key and potentially go through the effort to decrypt the values.
Final Recommendation
If you are making modifications to the sample code provided, please keep in mind that Functions retain console warnings and errors within internal Twilio systems and in the Twilio Debugger for some time.
Do not use any of the following console logging methods with any sensitive, unencrypted data:
Conclusion
In this lesson, you learned how you can protect data collected from <Gather> TwiML with encryption via a Serverless Function and redaction through Voice PCI Mode.
If you want to collect payments from your callers, consider the fully PCI-compliant Twilio <Pay> feature.
To learn more about PCI compliance at Twilio, check out the documentation and responsibility matrix.
Users trust you to keep their sensitive information private. Make sure you respect and retain that trust by doing all you can to secure the data you process.
Bry Schinina is a developer and educator who deeply appreciates when companies don’t expose private information. She works as Tech Lead and Sr. Technical Account Manager at Twilio, solving complex problems and helping organizations succeed with their digital engagement platform. She can be reached at bschinina [at] twilio.com.
Related Posts
Related Resources
Twilio Docs
From APIs to SDKs to sample apps
API reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.
Resource Center
The latest ebooks, industry reports, and webinars
Learn from customer engagement experts to improve your own communication.
Ahoy
Twilio's developer community hub
Best practices, code samples, and inspiration to build communications and digital engagement experiences.