LoginSignup
2
2

More than 5 years have passed since last update.

TwilioからのリクエストであるかチェックするのはFormValidation上で行うとスマートな感じがする

Posted at

このページについて

Twilioからのwebhookが、本当にTwilioサーバーから来たのか検査するのは、
FormValidationで行うのがイイんじゃない?という話。

が、何でこれを書こうと思ったのか覚えていない。
下書き漁ったらあったので、とりあえず書いてみます。

環境

  • PHP 7.1
  • Laravel 5.5
  • Twilio SDK 5.16

Twilioサーバーから来たリクエストか検査する方法

本家:https://jp.twilio.com/docs/api/security

バリデーションの手順

  1. 対象の完全URL、プロトコルから始まり、全クエリーストリングを含むもの (https から始まり、 ? で指定するものを全て含む) を取得します。
  2. もしリクエストがPOSTの場合、全てのPOSTパラメータをUnix形式の大文字小文字を区別するソート方法で、アルファベット順に並び替えます。
  3. ソートしたPOSTパラメータに、URLの最後まで繰り返しパラメータ名と値(区切り文字を使わない)を当て込んでいきます。
  4. 最終URLの文字列に、ユーザの AuthToken を鍵として(大文字小文字を区別します)、HMAC-SHA1 形式で署名します。
  5. 結果のハッシュ値をBase64でエンコードしてください。
  6. 結果のハッシュ値とTwilio側が署名した X-Twilio-Signatureヘッダを比較します。 これらが一致すれば問題ありません。

だそうです。
めんどくさくてやる気にならない・・・

でも大丈夫、僕たちにはtwilio-sdkがある

Twilio-SDKを使った実装

\Twilio\Security\RequestValidatorクラスを使う

validation.php
public function twilioValidation()
{
    $token = '12345';

    $validator = new RequestValidator($token);
    return $validator->validate($_SERVER['HTTP_X_TWILIO_SIGNATURE'], url()->current(), $_POST);
}

$tokenはTwilioのトークン情報で、本来はDBや設定ファイルに持ってる内容です。
ちなみにこのメソッド、GETでwebhook受けるようにしてると動かないので、
Twilioサーバーからのリクエストかを検査したい箇所は、全てPOSTで受ける必要があります。

RequestValidator::validateについて、
* 第1引数:固定
* 第2引数:webhookのurl 検査という意味だと固定値持たせた方が良いのかもしれないけど、受けたリクエストのURLを突っ込む
* 第3引数:POSTパラメータは全て渡す必要があるため、$_POSTで固定

実装自体はこれだけでOKなのですが、これをどこに置くのが良いか?
というのがこのページの主題です。

バリデーションの置き場

ミドルウェア

最初に思ったのはここ。
Twilioとやり取りするルーター定義に、ミドルウェアで設定して一律やったら楽じゃないかと。
設定はルーティングファイルに書けば良いから、バリデーション目的ならここでも構わなかったりする。
が、ここに書くとPOSTされたデータの吸い上げを、コントローラ側の各処理でやらないとイケないから、
それは案外イケてないのではないかと思って辞めました。

バリデーション(独自ルール)

じゃあ、ということでバリデーションに実装することにしました。
独自のルールとして作るかなぁ、と。
これはあっさり辞めました。
だって、特定のデータに依存しないから。

バリデーション(FormValidation::autorize)

で、辿り着いたのがここ。
Laravelの認可の標準的な考え方とはずれはするけど、
Twilioサーバーからのリクエストなら認可する、という考え方をするならここだな、と。
ここに置けば、コントローラに渡すRequestの属性値も同時に設定できるし、割とスマートかな、と。

TwilioRequest.php

use Illuminate\Foundation\Http\FormRequest;
use Twilio\Security\RequestValidator;

class TwilioRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     * @throws \Exception
     */
    public function authorize()
    {
        $token = '12345';

        $validator = new RequestValidator($token);
        return $validator->validate($_SERVER['HTTP_X_TWILIO_SIGNATURE'], url()->current(), $_POST);
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'CallSid' => 'required',
            'Caller' => 'required',
            'Digits' => '',
            'From' => 'required',
            'To' => 'required',
        ];
    }
}

全容はこんな感じ。

rulesで返している値を見れば、コントローラ側でTwilioのどのパラメータを使っているか判るのがミソ。
使い方は、コントローラのメソッドの引数にこのクラスを書いとくだけ。
webhookで受けるメソッド全てに上記を設定しないといけないけど、
使用しているTwilioのパラメータが何か判ることの方がメリットが大きいと判断しました。

おわりに

Twilioとのやり取りではなくて、
バリデーションの方法について記載してみました。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2