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

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

With a few lines of code we’ll write a custom middleware for our Lumen app that uses the Twilio PHP SDK’s RequestValidator utility. We can then use that middleware on our Lumen routes which accept Twilio webhooks to confirm that incoming requests genuinely originated from Twilio.

Let’s get started!

Create custom middleware

Twilio PHP SDKには受信リクエストに使用できる RequestValidator ユーティリティーが含まれています。

各Lumenルートにリクエスト検証コードを含めることができますが、これはLumenミドルウェアを記述する絶好の機会です。 こうすれば、Twilioからの受信リクエストを受け付ける全ルートで検証ロジックを再利用できます。

To validate an incoming request genuinely originated from Twilio, we need to call the $requestValidator->validate(...). That method will return true if the request is valid or false if it isn’t. Our middleware then either continues processing the view or returns a 403 HTTP response for unauthorized requests.

コードサンプルを読み込んでいます...
Language
SDKバージョン:
  • 5.x
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Response;
use Twilio\Security\RequestValidator;

class TwilioRequestValidator
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
      // Be sure TWILIO_APP_TOKEN is set in your .env file.
      // You can get your app token in your twilio console https://www.twilio.com/console
      $requestValidator = new RequestValidator(env('TWILIO_APP_TOKEN'));

      $isValid = $requestValidator->validate(
        $request->header('X-Twilio-Signature'),
        $request->fullUrl(),
        $request->toArray()
      );

      if ($isValid) {
        return $next($request);
      } else {
        return new Response('You are not Twilio :(', 403);
      }
    }
}
Twilio SDKの 'RequestValidator' を使用してWebhookリクエストを検証する
Lumenミドルウェアを作成してリクエストを検証する

Twilio SDKの 'RequestValidator' を使用してWebhookリクエストを検証する

Webhookにリクエスト検証ミドルウェアを適用する

カスタムのTwilioリクエスト検証ミドルウェアを、Twilio のWebhookで使用される全Lumenルートに適用します。

ルートでミドルウェアを使用するには、まずミドルウェアをRegister Middlewareセクションのbootstrap/app.phpに追加することが必要です。

$app->routeMiddleware([
  'TwilioRequestValidator' => App\Http\Middleware\TwilioRequestValidator::class,
]);

続いて、下記のようにミドルウェアを各ルートに追加することが必要です。

コードサンプルを読み込んでいます...
Language
SDKバージョン:
  • 5.x
<?php

use Illuminate\Http\Request;
use Twilio\Twiml;

// Note: $app was changed for $router since Lumen 5.5.0
// Reference: https://lumen.laravel.com/docs/5.5/upgrade#upgrade-5.5.0

$router->post('voice', ['middleware' => 'TwilioRequestValidator',
  function() {
    $twiml = new Twiml();
    $twiml->say('Hello World!');

    return response($twiml)->header('Content-Type', 'text/xml');
  }
]);

$router->post('message', ['middleware' => 'TwilioRequestValidator',
  function(Request $request) {
    $bodyLength = strlen($request->input('Body'));

    $twiml = new Twiml();
    $twiml->message("Your text to me was $bodyLength characters long. ".
                    "Webhooks are neat :)");

    return response($twiml)->header('Content-Type', 'text/xml');
  }
]);
Creates a route for /voice and /message to handle the respective webhooks.
Lumenルートを作成し、Twilioリクエストを処理する

Creates a route for /voice and /message to handle the respective webhooks.

実際に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

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

To fix this problem we recommend adding an extra check in your middleware, like shown here, telling it to only reject incoming requests if your app is running in production.

コードサンプルを読み込んでいます...
Language
SDKバージョン:
  • 5.x
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Response;
use Twilio\Security\RequestValidator;

class TwilioRequestValidator
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
      if (env('APP_ENV') === 'test') {
        return $next($request);
      }

      // Be sure TWILIO_APP_TOKEN is set in your .env file.
      // You can get your app token in your twilio console https://www.twilio.com/console
      $requestValidator = new RequestValidator(env('TWILIO_APP_TOKEN'));

      $isValid = $requestValidator->validate(
        $request->header('X-Twilio-Signature'),
        $request->fullUrl(),
        $request->toArray()
      );

      if ($isValid) {
        return $next($request);
      } else {
        return new Response('You are not Twilio :(', 403);
      }
    }
}
Use `APP_ENV` environment variable to disable request validation.
Disable Twilio request validation when testing

Use `APP_ENV` environment variable to disable request validation.

次は?

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

PHPの開発環境のセットアップについて詳しく学ぶ。

Lumenアプリケーションをセキュアにする方法について学ぶには、一般的にはLumen公式ドキュメントのセキュリティ考慮事項についてのページをご覧ください。

Jose Oliveros
Andrew Baker
Kat King
Agustin Camino
Kevin Whinnery

ヘルプが必要ですか?

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

コードサンプルを読み込んでいます...
SDKバージョン:
  • 5.x
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Response;
use Twilio\Security\RequestValidator;

class TwilioRequestValidator
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
      // Be sure TWILIO_APP_TOKEN is set in your .env file.
      // You can get your app token in your twilio console https://www.twilio.com/console
      $requestValidator = new RequestValidator(env('TWILIO_APP_TOKEN'));

      $isValid = $requestValidator->validate(
        $request->header('X-Twilio-Signature'),
        $request->fullUrl(),
        $request->toArray()
      );

      if ($isValid) {
        return $next($request);
      } else {
        return new Response('You are not Twilio :(', 403);
      }
    }
}
SDKバージョン:
  • 5.x
<?php

use Illuminate\Http\Request;
use Twilio\Twiml;

// Note: $app was changed for $router since Lumen 5.5.0
// Reference: https://lumen.laravel.com/docs/5.5/upgrade#upgrade-5.5.0

$router->post('voice', ['middleware' => 'TwilioRequestValidator',
  function() {
    $twiml = new Twiml();
    $twiml->say('Hello World!');

    return response($twiml)->header('Content-Type', 'text/xml');
  }
]);

$router->post('message', ['middleware' => 'TwilioRequestValidator',
  function(Request $request) {
    $bodyLength = strlen($request->input('Body'));

    $twiml = new Twiml();
    $twiml->message("Your text to me was $bodyLength characters long. ".
                    "Webhooks are neat :)");

    return response($twiml)->header('Content-Type', 'text/xml');
  }
]);
SDKバージョン:
  • 5.x
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Response;
use Twilio\Security\RequestValidator;

class TwilioRequestValidator
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
      if (env('APP_ENV') === 'test') {
        return $next($request);
      }

      // Be sure TWILIO_APP_TOKEN is set in your .env file.
      // You can get your app token in your twilio console https://www.twilio.com/console
      $requestValidator = new RequestValidator(env('TWILIO_APP_TOKEN'));

      $isValid = $requestValidator->validate(
        $request->header('X-Twilio-Signature'),
        $request->fullUrl(),
        $request->toArray()
      );

      if ($isValid) {
        return $next($request);
      } else {
        return new Response('You are not Twilio :(', 403);
      }
    }
}