Twilioの着信リクエストを検証してSinatraアプリケーションを安全にする

このガイドでは、開発者のTwilio Webhookへの受信リクエストが本当にTwilioからのものか検証することで、Sinatraアプリケーションをセキュアにする方法を取り上げます。

数行のコードでTwilio Ruby SDKの検証ユーティリティーを使用するSinatraアプリケーション用のカスタム検証を記述します。 そうすればこのバリデーターをTwilioのWebhookを受け入れるSinatraアプリケーション上で使用し、受信リクエストが確かにTwilioが発信源となっているか確認します。

Let’s get started!

開始ポイント

Ruby用の開発環境のセットアップについてヘルプが必要な場合は、セットアップドキュメントをご確認ください。

Sinatraアプリケーションを実装している基本的なコードサンプルから構築をはじめます。

次のようにサンプルコードを実行する:

$ ruby index.rb

このシンプルなアプリケーションは任意の「/」へのリクエストに対して、検証を一切行わずにTwiMLを返します:

$ curl -XPOST http://localhost:4567
<?xml version="1.0" encoding="UTF-8"?> <Response><Message>Hello World</Message></Response>
コードサンプルを読み込んでいます...
Language
SDKバージョン:
  • 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
TwiMLに応答するシンプルなSinatraアプリケーション
Twilioリクエスト検証を行わないSinatraアプリケーション

TwiMLに応答するシンプルなSinatraアプリケーション

リクエスト検証を追加する

Sinatraアプリケーションにリクエスト検証を追加するには認証トークンが必要です。

Twilio Consoleから取得できるTwilio API用の認証トークンが必要になります。 このトークンは新しい環境変数にエクスポートすることで設定されます:

$ export TWILIO_AUTH_TOKEN=" TWILIO_AUTH_TOKEN_HERE "

Rackミドルウェアを通じてリクエスト検証を使用するには、下記の行を追加することが必要です:

use Rack::TwilioWebhookAuthentication, ENV['TWILIO_AUTH_TOKEN'], '/'
コードサンプルを読み込んでいます...
Language
SDKバージョン:
  • 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
この検証コードで、Sinatraアプリケーションへの受信リクエストが本物かどうか確認します。
Twilioリクエストを検証るすrSinatraアプリケーション用のTwilio Webhookミドルウェア

この検証コードで、Sinatraアプリケーションへの受信リクエストが本物かどうか確認します。

概要とテスト

この時点でサンプルは発展し、認証トークンを使用することでTwilioリクエストにに対するセキュアな認証が確保されました。

前項のcurlの手順を繰り返すことで、認証が機能していることをテストできます:

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

カスタム検証ロジックで、Sinatraアプリケーションへの受信リクエストが本物かどうか確認します。 もしリクエストが有効な場合は<?xml version="1.0" encoding="UTF-8"?> <Response><Message>Hello World</Message></Response>を、そうでない場合はTwilio Request Validation Failed.を返します。 このロジックではリクエストの処理を継続するか、無効なリクエストの試行に対しては403 HTTPレスポンスを返します。

テスト中の検証

あなたのSinatraアプリケーション用にテストを記述する場合、Twilioのリクエスト検証を使用するルートに対しては失敗する可能性があります。 この問題を修正するには、テストでライブラリーを模倣することを推奨します。 リクエストの模倣レスポンスの模倣については公式のRackドキュメントを参照してください。

次は?

開発者のTwilio Webhookへのリクエストの検証はTwilioアプリケーションをセキュアにするのには素晴らしい第一歩です。 アプリケーションの保護についてのさらなるアドバイスについては、セキュリティードキュメントの全部、特に対詐欺行為対策開発者ガイドをお読みいただくことをお勧めします。

一般的なSinatraアプリケーションのセキュリティーを向上させる方法の詳細については、公式Sinatraドキュメント内のセキュリティー検討事項のページを参照するか、公式RackドキュメントTwilio Ruby SDKをご覧いただけます。

Juan Carlos Ojeda
Andrew Baker
Kevin Whinnery
Kat King
Paul Kamp
Agustin Camino

ヘルプが必要ですか?

誰しもが一度は考える「コーディングって難しい」。そんな時は、お問い合わせフォームから質問してください。 または、Stack Overflow でTwilioタグのついた情報から欲しいものを探してみましょう。

コードサンプルを読み込んでいます...
SDKバージョン:
  • 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バージョン:
  • 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