Secure your Sinatra app by validating incoming Twilio requests

In this guide we will cover how to secure your Sinatra application by validating incoming requests to your Twilio webhooks are, in fact, from Twilio.

With a few lines of code we will write a custom validation for our Sinatra app that uses the Twilio Ruby SDK validator utility. We can then use that validator on our Sinatra app which accept Twilio webhooks to confirm that incoming requests genuinely originated from Twilio.

Let’s get started!

An starting point

If you need help with the setup of a development environment for ruby check the setup documentation.

We will start building from a basic code example implementing a Sinatra application.

Running the example code like:

$ ruby index.rb

This simple application returns TwiML to any request to "/" without any validation whatsoever:

$ curl -XPOST http://localhost:4567
<?xml version="1.0" encoding="UTF-8"?> <Response><Message>Hello World</Message></Response>
Loading Code Samples...
Language
SDK Version:
  • 3.x
  • 5.x
# You can find your Twilio Auth Token here: https://www.twilio.com/console
# Set at runtime as follows:
# $ TWILIO_AUTH_TOKEN="XXXXXXXXXXXXXXXXXXX" ruby index.rb
#
# This will not work unless you export the TWILIO_AUTH_TOKEN environment
# variable.

require 'sinatra'
require 'twilio-ruby'

post '/' do
  content_type 'text/xml'

  response = Twilio::TwiML::Response.new do |r|
    r.Message('Hello World')
  end
  response.to_xml
end
# You can find your Twilio Auth Token here: https://www.twilio.com/console
# Set at runtime as follows:
# $ TWILIO_AUTH_TOKEN="XXXXXXXXXXXXXXXXXXX" ruby index.rb
#
# This will not work unless you export the TWILIO_AUTH_TOKEN environment
# variable.

require 'sinatra'
require 'twilio-ruby'

post '/' do
  content_type 'text/xml'

  response = Twilio::TwiML::MessagingResponse.new
  response.message('Hello World')

  response
end
A simple Sinatra application responding TwiML.
A Sinatra app without Twilio requests validation

A simple Sinatra application responding TwiML.

Adding request validation

To add request validation to your Sinatra App you'll need an authentication token.

We will need an Authentication Token for Twilio API from the Twilio Console, this token will be set by exporting a new environment variable:

$ export TWILIO_AUTH_TOKEN=" TWILIO_AUTH_TOKEN_HERE "

To use the request validation through the Rack middleware we have to add the following line:

use Rack::TwilioWebhookAuthentication, ENV['TWILIO_AUTH_TOKEN'], '/'
Loading Code Samples...
Language
SDK Version:
  • 3.x
  • 5.x
# You can find your Twilio Auth Token here: https://www.twilio.com/console
# Set at runtime as follows:
# $ TWILIO_AUTH_TOKEN="XXXXXXXXXXXXXXXXXXX" ruby index.rb
#
# This will not work unless you export the TWILIO_AUTH_TOKEN environment
# variable.

require 'sinatra'
require 'twilio-ruby'
require 'rack'

use Rack::TwilioWebhookAuthentication, ENV['TWILIO_AUTH_TOKEN'], '/'

post '/' do
  content_type 'text/xml'

  response = Twilio::TwiML::Response.new do |r|
    r.Message('Hello World')
  end
  response.to_xml
end
# You can find your Twilio Auth Token here: https://www.twilio.com/console
# Set at runtime as follows:
# $ TWILIO_AUTH_TOKEN="XXXXXXXXXXXXXXXXXXX" ruby index.rb
#
# This will not work unless you export the TWILIO_AUTH_TOKEN environment
# variable.

require 'sinatra'
require 'twilio-ruby'
require 'rack'

use Rack::TwilioWebhookAuthentication, ENV['TWILIO_AUTH_TOKEN'], '/'

post '/' do
  content_type 'text/xml'

  response = Twilio::TwiML::MessagingResponse.new
  response.message(body: 'Store Location: 123 Easy St.')

  response
end
Confirm incoming requests to your Sinatra app are genuine with this validation.
Use Twilio webhook middleware for Sinatra app that validates Twilio requests

Confirm incoming requests to your Sinatra app are genuine with this validation.

Overview and testing

At this point the example has grown and has enabled the secure authentication for Twilio requests using your authentication token.

We can test that the authentication is working by repeating the previous curl step:

$ curl -XPOST http://localhost:4567
Twilio Request Validation Failed.

Confirming incoming requests to your Sinatra application are genuine with this custom validation logic. That will return <?xml version="1.0" encoding="UTF-8"?> <Response><Message>Hello World</Message></Response> if the request is valid or Twilio Request Validation Failed. if it is not. Our logic then either continues processing the request or returns error 403 HTTP response for invalid requests attempt.

Validation during testing

If you write tests for your sinatra application those tests may fail for routes where you use the Twilio request validation. To fix this problem we recommend to use a mocking library in your tests. Take a look at the official Rack documentation for mocking requests and mocking responses.

What’s next?

Validating requests to your Twilio webhooks is a great first step for securing your Twilio application. We recommend reading over our full security documentation for more advice on protecting your app, and the Anti-Fraud Developer’s Guide in particular.

To learn more about securing your Sinatra application in general, check out the security considerations page in the official Sinatra documentation, or you can also take a look at the official Rack documentation and the Twilio Ruby SDK.

Juan Carlos Ojeda
Andrew Baker
Agustin Camino
Kevin Whinnery

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.

1 / 1
Loading Code Samples...
SDK Version:
  • 3.x
  • 5.x
# You can find your Twilio Auth Token here: https://www.twilio.com/console
# Set at runtime as follows:
# $ TWILIO_AUTH_TOKEN="XXXXXXXXXXXXXXXXXXX" ruby index.rb
#
# This will not work unless you export the TWILIO_AUTH_TOKEN environment
# variable.

require 'sinatra'
require 'twilio-ruby'

post '/' do
  content_type 'text/xml'

  response = Twilio::TwiML::Response.new do |r|
    r.Message('Hello World')
  end
  response.to_xml
end
# You can find your Twilio Auth Token here: https://www.twilio.com/console
# Set at runtime as follows:
# $ TWILIO_AUTH_TOKEN="XXXXXXXXXXXXXXXXXXX" ruby index.rb
#
# This will not work unless you export the TWILIO_AUTH_TOKEN environment
# variable.

require 'sinatra'
require 'twilio-ruby'

post '/' do
  content_type 'text/xml'

  response = Twilio::TwiML::MessagingResponse.new
  response.message('Hello World')

  response
end
SDK Version:
  • 3.x
  • 5.x
# You can find your Twilio Auth Token here: https://www.twilio.com/console
# Set at runtime as follows:
# $ TWILIO_AUTH_TOKEN="XXXXXXXXXXXXXXXXXXX" ruby index.rb
#
# This will not work unless you export the TWILIO_AUTH_TOKEN environment
# variable.

require 'sinatra'
require 'twilio-ruby'
require 'rack'

use Rack::TwilioWebhookAuthentication, ENV['TWILIO_AUTH_TOKEN'], '/'

post '/' do
  content_type 'text/xml'

  response = Twilio::TwiML::Response.new do |r|
    r.Message('Hello World')
  end
  response.to_xml
end
# You can find your Twilio Auth Token here: https://www.twilio.com/console
# Set at runtime as follows:
# $ TWILIO_AUTH_TOKEN="XXXXXXXXXXXXXXXXXXX" ruby index.rb
#
# This will not work unless you export the TWILIO_AUTH_TOKEN environment
# variable.

require 'sinatra'
require 'twilio-ruby'
require 'rack'

use Rack::TwilioWebhookAuthentication, ENV['TWILIO_AUTH_TOKEN'], '/'

post '/' do
  content_type 'text/xml'

  response = Twilio::TwiML::MessagingResponse.new
  response.message(body: 'Store Location: 123 Easy St.')

  response
end