このページについて
Twilioからのwebhookが、本当にTwilioサーバーから来たのか検査するのは、
FormValidationで行うのがイイんじゃない?という話。
が、何でこれを書こうと思ったのか覚えていない。
下書き漁ったらあったので、とりあえず書いてみます。
環境
- PHP 7.1
- Laravel 5.5
- Twilio SDK 5.16
Twilioサーバーから来たリクエストか検査する方法
本家:https://jp.twilio.com/docs/api/security
バリデーションの手順
- 対象の完全URL、プロトコルから始まり、全クエリーストリングを含むもの (https から始まり、 ? で指定するものを全て含む) を取得します。
- もしリクエストがPOSTの場合、全てのPOSTパラメータをUnix形式の大文字小文字を区別するソート方法で、アルファベット順に並び替えます。
- ソートしたPOSTパラメータに、URLの最後まで繰り返しパラメータ名と値(区切り文字を使わない)を当て込んでいきます。
- 最終URLの文字列に、ユーザの AuthToken を鍵として(大文字小文字を区別します)、HMAC-SHA1 形式で署名します。
- 結果のハッシュ値をBase64でエンコードしてください。
- 結果のハッシュ値とTwilio側が署名した X-Twilio-Signatureヘッダを比較します。 これらが一致すれば問題ありません。
だそうです。
めんどくさくてやる気にならない・・・
でも大丈夫、僕たちにはtwilio-sdkがある
Twilio-SDKを使った実装
\Twilio\Security\RequestValidatorクラスを使う
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の属性値も同時に設定できるし、割とスマートかな、と。
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とのやり取りではなくて、
バリデーションの方法について記載してみました。