前回の記事 "CakePHP でドメインの異なるサイトでセッションを共有する" で、 CakePHP のアプリケーション間でセッションのユーザ情報を共有するシンプルな方法について書きました。 CakePHP 開発チームによって、CakePHP はすでにセキュアなセッション・ハンドリングを備えていますが、なかにはリンクにセッションIDを直接付加するのは安全ではないと考える開発者の方もいるでしょう。サーチエンジンのなかにはセッションIDを付加した URL ごとインデックスしてしまうものもあると聞きます。本当に「やばい」ですネ。ソリューションとしては、ページ上のいかなるリンクにもセッションIDを付加しないことです。
まず、siteA.com と siteB.com、2つのサイトがあると想定します。 Auth Component で認証されたユーザが、 siteA.com のページ上のリンクをクリックして、セッションのユーザ情報を保持したまま siteB.com にリダイレクトすることが今回の主旨です。
まず初めに、設定することがいくつかあります。:
- siteB.com のコアファイル (/app/config/core.php) の 'Security.level' を 'low' に設定します。
- セッション・ハンドリング・メソッド(app/config/core.php 内の 'Session.save') を 'database' にします。
- 両サイトとも同じ Security.salt (/app/config/core.php) を使用します。
両サイトとも同じセッションデータベースを使うところがミソです。
CakePHP のコンソール でセッションテーブルを作ります。
$ cake schema run create Sessions
SQL ファイル (app/config/sql/sessions.sql) でも作成できます。
siteA.com と siteB.com それぞれのアプリケーション内に SiteTransfer モデルを作成します。
class SiteTransfer extends AppModel {
var $name = 'SiteTransfer';
}
基本的に、site_transfers は以下のようなテーブル構造でよいでしょう。
create table site_transfers ( id varchar(36) not null, sess_id varchar(26) not null, primary key (id) );
siteA.com に Users コントローラを作ります。
class UsersController extends AppController {
var $name = 'Users';
function index() {}
function redirectem() {
$this->autoRender = false;
App::import('Core', 'String');
$data['SiteTransfer']['sess_id'] = $this->Session->id();
$this->SiteTransfer->id = String::uuid();
if($this->SiteTransfer->save($data)) {
$this->redirect('http://siteB.com/users/catchem?uuid='.$this->SiteTransfer->id);
}
}
}
ビューの index.ctp に‘Go to siteB.com’というリンクを貼ります。
echo $html->link('Go to siteB.com', array(
'action' => 'redirectem'
));
ここでどういう仕組みかを見ていきましょう。
siteA.com の Index ページにいる認証済みユーザがリンクをクリックすると, redirectem() アクション (このアクションにビューは必要ありません) にリダイレクトします。 この redirectem() メソッドでは, 現在のセッションID を取得して、site_transfers に UUID (例えば String::uuid を使用) で保存する処理をします。そして、/users/catchem on siteB.com にリダイレクトします。
それでは次に、siteB.comUser の Users コントローラを作成しましょう。
class UsersController extends AppController {
var $name = 'Users';
var $components = array('Session');
function beforeFilter() {
if(!empty($this->params['url']['uuid'])) {
$uuid = $this->params['url']['uuid'];
$data = $this->SiteTransfer->findById($uuid);
$this->Session->id($data['SiteTransfer']['sess_id']);
$this->SiteTransfer->del($uuid);
}
}
function catchem() {
$this->redirect(array('action' => 'index'));
}
function index() {
pr($this->Session->read('Auth'));
exit;
}
}
ユーザが siteB.com にリダイレクトしたとき、何が起きるのでしょう?
UUID をトークンとして site_transfers テーブルを検索します。一致したレコードからセッションを生成します。最後にセキュリティ上、site_transfers テーブルからレコード(セッションID と UUID)を削除します。
終了です。これでユーザは siteA.com と siteB.com、両サイトでセッションのユーザ情報を共有できました。