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

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

Twilio Node SDKを使用してExpressアプリケーションをセキュアにする方法はシンプルです。 Twilio SDKにはすぐに使えるExpressミドルウェアがついてきます。

Let’s get started!

Twilio Expressリクエスト検証ミドルウェア

Twilio Node SDKには受信リクエストを検証するExpressミドルウェアとして使用できる webhook() メソッドが含まれています。 Expressのルートに適用されると、リクエストが認証されない場合にはミドルウェアは403 HTTPレスポンスを返します。

コードサンプルを読み込んでいます...
Language
SDKバージョン:
  • 3.x
// You can find your Twilio Auth Token here: https://www.twilio.com/console
// Set at runtime as follows:
// $ TWILIO_AUTH_TOKEN=XXXXXXXXXXXXXXXXXXX node index.js
//
// This will not work unless you set the TWILIO_AUTH_TOKEN environment
// variable.

const twilio = require('twilio');
const app = require('express')();
const bodyParser = require('body-parser');
const VoiceResponse = require('twilio').twiml.VoiceResponse;
const MessagingResponse = require('twilio').twiml.MessagingResponse;

app.use(bodyParser.urlencoded({ extended: false }));

app.post('/voice', twilio.webhook(), (req, res) => {
  // Twilio Voice URL - receives incoming calls from Twilio
  const response = new VoiceResponse();

  response.say(
    `Thanks for calling!
     Your phone number is ${req.body.From}. I got your call because of Twilio´s
     webhook. Goodbye!`
  );

  res.set('Content-Type', 'text/xml');
  res.send(response.toString());
});

app.post('/message', twilio.webhook(), (req, res) => {
  // Twilio Messaging URL - receives incoming messages from Twilio
  const response = new MessagingResponse();

  response.message(`Your text to me was ${req.body.Body.length} characters long.
                    Webhooks are neat :)`);

  res.set('Content-Type', 'text/xml');
  res.send(response.toString());
});

app.listen(3000);
本カスタムミドルウェアで、開発者のExpressルートへの受信リクエストが本当にTwilioから発信されたものかどうか確認します。
Twilioリクエストの検証を行うExpressアプリケーション用にTwilio Webhookミドルウェアを使用する

本カスタムミドルウェアで、開発者のExpressルートへの受信リクエストが本当にTwilioから発信されたものかどうか確認します。

実際にTwilioのWebhookを使用できるよう、ローカルの開発環境にトンネルを使用する

If your Twilio webhook URLs start with https:// instead of http://, your request validator may fail locally when you use ngrok or in production if your stack terminates SSL connections upstream from your app. This is because the request URL that your Express application sees does not match the URL Twilio used to reach your application.

ngrokでローカル開発向けにこれを修正するには、ngrok https 3000ではなくngrok http 3000を使用し、Webhookへのリクエストを受け入れます。

Disable request validation during testing

Expressルート用にテストを記述した場合、Twilioリクエスト検証ミドルウェアを使用するルートでテストに失敗する可能性があります。 これらルートに送信するテストスイートのいかなるリクエストも、ミドルウェアの検証チェックに失敗します。

この問題を修正するには、 {validate: false} を検証ミドルウェア twilio.webhook() に渡して無効化します。 Expressアプリケーションでは、NODE_ENV をアプリケーションが実行中の環境を決定する値として使用するのは一般的なことです。 コード例では、NODE_ENV'test' のとき、検証ミドルウェアが無効化されます。

コードサンプルを読み込んでいます...
Language
SDKバージョン:
  • 3.x
// You can find your Twilio Auth Token here: https://www.twilio.com/console
// Set at runtime as follows:
// $ TWILIO_AUTH_TOKEN=XXXXXXXXXXXXXXXXXXX node index.js
//
// This will not work unless you set the TWILIO_AUTH_TOKEN environment
// variable.

const twilio = require('twilio');
const app = require('express')();
const bodyParser = require('body-parser');
const VoiceResponse = require('twilio').twiml.VoiceResponse;
const MessagingResponse = require('twilio').twiml.MessagingResponse;

const shouldValidate = process.env.NODE_ENV !== 'test';

app.use(bodyParser.urlencoded({ extended: false }));

app.post('/voice', twilio.webhook({ validate: shouldValidate }), (req, res) => {
  // Twilio Voice URL - receives incoming calls from Twilio
  const response = new VoiceResponse();

  response.say(
    `Thanks for calling!
     Your phone number is ${req.body.From}. I got your call because of Twilio´s
     webhook. Goodbye!`
  );

  res.set('Content-Type', 'text/xml');
  res.send(response.toString());
});

app.post(
  '/message',
  twilio.webhook({ validate: shouldValidate }),
  (req, res) => {
    // Twilio Messaging URL - receives incoming messages from Twilio
    const response = new MessagingResponse();

    response.message(`Your text to me was ${req.body.Body
      .length} characters long.
                    Webhooks are neat :)`);

    res.set('Content-Type', 'text/xml');
    res.send(response.toString());
  }
);

app.listen(3000);
環境変数を使用して、テスト中のWebhook検証を無効にします。
Disable Twilio webhook middleware when testing Express routes.

環境変数を使用して、テスト中のWebhook検証を無効にします。

次は?

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 Express application in general, check out the security considerations page in the official Express docs.

Agustin Camino
Andrew Baker
Kat King
Paul Kamp
Jose Oliveros
Kevin Whinnery

ヘルプが必要ですか?

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

コードサンプルを読み込んでいます...
SDKバージョン:
  • 3.x
// You can find your Twilio Auth Token here: https://www.twilio.com/console
// Set at runtime as follows:
// $ TWILIO_AUTH_TOKEN=XXXXXXXXXXXXXXXXXXX node index.js
//
// This will not work unless you set the TWILIO_AUTH_TOKEN environment
// variable.

const twilio = require('twilio');
const app = require('express')();
const bodyParser = require('body-parser');
const VoiceResponse = require('twilio').twiml.VoiceResponse;
const MessagingResponse = require('twilio').twiml.MessagingResponse;

app.use(bodyParser.urlencoded({ extended: false }));

app.post('/voice', twilio.webhook(), (req, res) => {
  // Twilio Voice URL - receives incoming calls from Twilio
  const response = new VoiceResponse();

  response.say(
    `Thanks for calling!
     Your phone number is ${req.body.From}. I got your call because of Twilio´s
     webhook. Goodbye!`
  );

  res.set('Content-Type', 'text/xml');
  res.send(response.toString());
});

app.post('/message', twilio.webhook(), (req, res) => {
  // Twilio Messaging URL - receives incoming messages from Twilio
  const response = new MessagingResponse();

  response.message(`Your text to me was ${req.body.Body.length} characters long.
                    Webhooks are neat :)`);

  res.set('Content-Type', 'text/xml');
  res.send(response.toString());
});

app.listen(3000);
SDKバージョン:
  • 3.x
// You can find your Twilio Auth Token here: https://www.twilio.com/console
// Set at runtime as follows:
// $ TWILIO_AUTH_TOKEN=XXXXXXXXXXXXXXXXXXX node index.js
//
// This will not work unless you set the TWILIO_AUTH_TOKEN environment
// variable.

const twilio = require('twilio');
const app = require('express')();
const bodyParser = require('body-parser');
const VoiceResponse = require('twilio').twiml.VoiceResponse;
const MessagingResponse = require('twilio').twiml.MessagingResponse;

const shouldValidate = process.env.NODE_ENV !== 'test';

app.use(bodyParser.urlencoded({ extended: false }));

app.post('/voice', twilio.webhook({ validate: shouldValidate }), (req, res) => {
  // Twilio Voice URL - receives incoming calls from Twilio
  const response = new VoiceResponse();

  response.say(
    `Thanks for calling!
     Your phone number is ${req.body.From}. I got your call because of Twilio´s
     webhook. Goodbye!`
  );

  res.set('Content-Type', 'text/xml');
  res.send(response.toString());
});

app.post(
  '/message',
  twilio.webhook({ validate: shouldValidate }),
  (req, res) => {
    // Twilio Messaging URL - receives incoming messages from Twilio
    const response = new MessagingResponse();

    response.message(`Your text to me was ${req.body.Body
      .length} characters long.
                    Webhooks are neat :)`);

    res.set('Content-Type', 'text/xml');
    res.send(response.toString());
  }
);

app.listen(3000);