Menu

Rate this page:

Thanks for rating this page!

We are always striving to improve our documentation quality, and your feedback is valuable to us. How could this documentation serve you better?

Secure your Python Amazon Lambda App by Validating Incoming Twilio Requests

Today we're going to look at how to secure your Amazon Lambda Python app by validating that incoming requests to your webhook are, in fact, actually coming from Twilio. We'll cover loading external Python libraries on Lambda, passing a header through Amazon API Gateway, and validating requests in Amazon Lambda with the Twilio Python Helper Library.

If you haven't yet visited our receiving and replying to messages in Python on Amazon Lambda guide, we highly suggest you visit that first. This application will build on top of the code and setup in that guide.

If you've already gone through that guide? Excellent... let's do some work to keep out the imposters!

Passing The X-Twilio-Signature Header through API Gateway

Our first stop on this security flavored trip is to the Amazon API Gateway console. In order to pass a received Header from API Gateway through to Amazon Lambda and our Python logic, we need to explicitly call it out.

From this point forward, we're going to assume you have built the example application from the receiving and replying to messages in Python on Amazon Lambda guide.

Modifying the Method Request to Keep X-Twilio Signature

Click through to your API, and from inside 'Resources' expand the Twilio API. At that point click on the 'POST' method that we set up in the Reply Guide:

Next, click the 'Method Request' link, and expand the 'HTTP Request Headers' section. In there, you should add one new header: X-Twilio-Signature. When you are done, it should look like this:

Passing a Header Through API Gateway

Return to Method Execution by clicking the link at the top of the frame - you may need to scroll up to see it.

Adding X-Twilio-Signature to the JSON Passed to our Lambda Function

Next, click the 'Integration Request' link. Expand the 'Body Mapping Templates' section, and click on the application/x-www-form-urlencoded mapping set up for the reply guide.

Change the mapping to pass through the X-Twilio-Signature with the following code:

#set($httpPost = $input.path('$').split("&"))
{
"twilioSignature": "$input.params('X-Twilio-Signature')",
#foreach( $kvPair in $httpPost )
 #set($kvTokenised = $kvPair.split("="))
 #if( $kvTokenised.size() > 1 )
   "$kvTokenised[0]" : "$kvTokenised[1]"#if( $foreach.hasNext ),#end
 #else
   "$kvTokenised[0]" : ""#if( $foreach.hasNext ),#end
 #end
#end
}

And that's it for API Gateway! Don't forget to Re-Deploy the API by selecting 'Deploy API' from the 'Actions' menu:

Deploy an API in API Gateway

Validating Requests in Amazon Lambda

Now, navigate to the Amazon Lambda Console and select your Twilio function.

You will need to write code and install packages on your development machine before loading everything to Lambda. On your computer, create a new directory with a single file named 'twilio_function.py'. Within that file, paste the contents of the default function from the Amazon console. We're going to look at how to use external libraries in Amazon Lambda!

Including the Twilio Python Helper Library in Amazon Lambda

Amazon has an excellent primer on how to add external Python packages to Lambda. We found using a global pip the easiest method of setup; if you'd like to try our method it follows this paragraph. If you don't, feel free to skip ahead to the next section.

  1. Use pip (for Python 2.7) with the -t (target) flag to install packages in your directory:
    pip install twilio -t /PATH/TO/NEW/DIRECTORY/WITH/TWILIO_FUNCTION.PY

    If your global pip is for 3.x, on OSX or *NIX run:
    python2.7 -m pip install twilio -t /PATH/TO/NEW/DIRECTORY/WITH/TWILIO_FUNCTION.PY

    If your global pip is for 3.x and you are on Windows, run:
    py -2.7 -m pip install twilio -t /PATH/TO/NEW/DIRECTORY/WITH/TWILIO_FUNCTION.PY
    Warning if on OSX with a Homebrew-managed pip Install: If pip refuses to install packages in this directory, create a new file in the directory named setup.cfg and the following contents:
    [install]
    prefix=​
    
  2. Zip all of the resulting files - including twilio_function.py and setup.cfg - in the directory. You should not include the parent directory, only the inside.
  3. Inside the Lambda console change the 'Code Entry Type' pulldown to 'Upload a .ZIP File'. Hit the 'Upload' button and navigate to the zip file we just created. The 'Save' button will upload the entire package.
  4. Change the 'Code Entry Type' back to 'Edit code inline'. Now, paste the complete contents (the 'Clipboard' button on this page will copy the whole function) of our example Twilio function into the box and 'Save' the function.
  5. From the configuration tab, change only the 'Handler' field to <FILENAME_MINUS_DOT_PY>.<FUNCTION_NAME>. If you're following our conventions, that should be 'twilio_function.handler':
    Lambda Function Handler
  6. 'Save', and hopefully you'll have no errors. If you do, double check you have only zipped the interior of the new directory and have a proper Handler and single check the other steps.

Once you're successful, we can move back to Lambda's Inline Editor in the 'Code' tab's 'Code Entry Type'.

Feel free to copy the complete twilio_function.py into your Inline Editor at this point. We're going to cover some of the sections piecemeal so you can better see how validation works. Finally, we're going to set a few environmental variables directly from the console and then we'll be ready to test!

Preparing to Validate Twilio Requests

While many Python web frameworks such as Flask and Django (see the 'Related Guides' section of the sidebar) handle dividing the body and headers and escaping and unescaping strings automatically, on Lambda you'll have to do a bit of preparation first. It's still rather simple since we can use some simple filtering and the built-in Python library urllib. Just note that the helper library expects unescaped strings and no headers in the passed dictionary.

Loading Code Sample...
      
      
          
          
          
          
        
      Preparing raw HTTP Parameters passed through API Gateway for Twilio Request Validation.

      Preparing Requests for Validation on Amazon Lambda

      Preparing raw HTTP Parameters passed through API Gateway for Twilio Request Validation.

      Validation itself is very simple. Behind the scenes, Twilio's helper library is hashing the incoming request with HMAC-SHA1 and your Auth Token (from the Twilio console) as a key to validate against X-Twilio-Signature. Additionally, we've added a simple check that the 'From' parameter matches a MASTER_NUMBER environmental variable, which might be a useful example for your application.

      Loading Code Sample...
          
          
              
              
              
              
            
          Using the Twilio Python Helper Library to validate a request on Amazon Lambda.

          Validating Requests in Python on Amazon Lambda

          Using the Twilio Python Helper Library to validate a request on Amazon Lambda.

          Now we just have to set some environmental variables, test our API, and hopefully get the secret message MMS'd to us. Carry on!

          Setting Environmental Variables on Amazon Lambda

          Environmental variables on Lambda are set directly from the console. In this application, we've demonstrated three - AUTH_TOKEN, MASTER_NUMBER, and REQUEST_URL. Directly below the Inline Editor, you should see an entry for environmental variables.

          In those fields, set AUTH_TOKEN to the authentication token from your Twilio Console. MASTER_NUMBER should be set to whatever number you will be using to test the application. REQUEST_URL is the same url you have given to Twilio as your webhook; if you need this, navigate to Numbers (#) in the console, click the number you are using, and check the primary handler.

          Here's where you would populate those variables:

          Environmental Variables in Amazon Lambda

          Great! Now let's make sure we keep the wrong folks out while letting you get the secret message.

          Text the message 'Secret' (capitalization doesn't matter) from your MASTER_NUMBER. If all goes well you should get a strangely reassuring MMS from an AI. If you text 'secret' from another number, you should see a slightly more unsettling SMS.

          And, just like in the Reply Guide, any message without a body of 'secret' will give a classic "Hello, World" response.

          What’s Next for Your App?

          Validating requests to your API Gateway webhooks in your Lambda function is an excellent step in your journey to a secure application. Next, we'd suggest reading our extensive security documentation for our best advice on securing your application. We particularly recommend the Anti-Fraud Developer's Guide, where we discuss some fraudulent behavior we've witnessed in the past.

          You're well on the path to a hardened Python application with Amazon Lambda now. We're very excited to see where you're headed next - let us know in the comments or through Twitter when you're ready to ship!

          Paul Kamp Jose Oliveros  David Prothero Kat King

          Need some help?

          We all do sometimes; code is hard. Get help now from our support team, or lean on the wisdom of the crowd browsing the Twilio tag on Stack Overflow.

          Loading Code Sample...