CakePHP2にTwilio SMSを使った2要素認証機能を追加

CakePHPアドベントカレンダー2014の2日目の記事です。12/1に @K1LoWさんが突然アドベントを始めたので乗り遅れないように2日目を担当します。まだアドベントカレンダーは空いているので皆様も是非。

そうそう、12/10にCakePHP2実践入門が電子書籍として発売されます。紙の本を出してから2年ぐらい経ちますが、これからCakePHP2を始める方には良いかなと思います。
http://gihyo.jp/news/nr/2014/12/0101


CakePHPのAuthコンポーネントを使うと簡単に認証機能が追加できます。詳細は日本語チュートリアルをご覧ください。

一般的なIDとパスワードを使った認証ですと、emailなどの共通したIDのシステムでパスワード使い回しのユーザがいる場合に標的になる可能性があります。これを防ぐために最近では2要素認証を導入しているところが多くなりました。GoogleDropboxZohoなど。
これらの多くはID/PW認証が完了した後に、SMSを送信してトークンを入力させて認証を行います。SMSが届く携帯電話などが必要です。

SMSの送信はTwilioを使うと簡単に実現できます。TwilioはPHP SDKを提供しているのでPHPに組み込むのも簡単です。具体的には下記のコードになります。

<?php
$client = new Services_Twilio($account_sid, $auth_token);
$message = $client->account->messages->sendMessage(
  $from_number, //Twilioで取得した電話番号(SMSはUS電話番号を使う必要があります)
  '+819011112222', //SMSの送信先携帯番号
  "sms_token: " . $token //SMSのメッセージ
);

あとはこのコードをAuthコンポーネントを使ったログイン用のコントローラアクションに挟むだけです。Authコンポーネント自体を変更したり継承してオーバライドする必要もありません。
CakePHPの2要素認証のサンプルをGithubに上げました。詳細はそちらをご覧ください。
https://github.com/ichikaway/cakephp-2FactorAuth-SMS


メインの処理は下記のコミットになります。
https://github.com/ichikaway/cakephp-2FactorAuth-SMS/commit/33f0ce45bbb47c95eafc5211cb0eb33cf6ba09e8

注意点としては、$this->Auth->login()でID/PW認証をしてしまうと、自動的に認証が通ったセッションが生成されてしまうため、いくらSMSトークン入力画面を挟んでも、ログイン後URLに直接遷移されてしまうと無意味になります。
ですので、このサンプルでは、

 $this->Auth->identify($this->request, $this->response);

を使って最初にID/PWのマッチングのみをしています。

ID/PWがマッチした場合にのみ、SMSを送信してトークン入力画面を表示します。トークンが送信されてユーザが入力し、トークンが一致すると、hiddenで引き継いだID/PWを使って$this->Auth->login()を実行してログイン成功のセッションを作成しています。
別にhiddenで引き継がなくてもSessionに入れて引き継いでも良いと思います。hiddenにしておくと$this->Auth->login()でそのまま利用できるため今回はそうしました。