Mojoliciousにおけるセッションの扱い

僕はWebアプリケーションをまともに作ったことがない人なので、Webアプリケーションのセキュリティ周りはうといです。Mojoliciousにはsessionというメソッドがあるのですが、これはWebアプリでいうところの、セッション管理であると信じていたのですが、ぜんぜん関係がない気がしてきました。

sessionというメソッドの名前ゆえに僕が勘違いしていた可能性が高いです。Mojoliciousのセッション管理機能は貧弱といわれるけれど、そもそもMojoliciousにはセッション管理機能がついていないんじゃないかと思えてきました。

sessionメソッドっていうのは、単純にクッキーに値を保存するための簡単な仕組みで、HMACによる署名がつくので、確実に改ざんを検出できるようになっています。

$c->session(name => 'kimoto');

ですから、クッキーに保存されるデータはJSONをBase64でエンコードしただけで、暗号化はされていません。ですのでデータは丸見えです。

けれどもHMACによる署名がされているので、信頼できない経路を通っても、改ざんを確実に検出することができます。

sessionメソッドを使っても、暗号化はされない。改ざんの検出が可能。

改ざんされないためには、secretの値をデフォルトの設定ではなく、きちんと変更しておくことが大切です。

$app->secret('#!=%JOLFIS#jf438nfj');

もし改ざんされている場合は、クッキーの値は破棄されます。

どんなデータをsessionに保存してよいか

一つ目は、見られてもよい重要でないデータです。暗号化されないので、見られたら困るデータは、sessionメソッドを使って保存してはいけません。見られたら困るデータは、サーバー側に保存しましょう。

二つ目は、セッションIDです。これには条件があって、一意であるという条件です。一度使ったセッションIDを再利用すると、セキュリティを維持することができないようです。

ですので、何らかのモジュールで、アプリケーションで一意になるセッションIDを生成してくれるモジュールを使いましょう。

# セッションIDの生成(一意であること)
my $session_id = Some::Module->new->create_session_id;

# セッションIDを署名付きクッキーに保存
$c->session(session_id => $session_id);

# 見られてもよいデータは、sessionに保存してもよい
$c->session(display => 'light');

# 見られてはいけないデータは、データベースなどへ
$dbi->insert({user_id => $user_id, age => 19});

追記

セッションを盗まれる可能性がありますが、それはクッキーにセッションIDを保存する以上は、どうにもならないです。これについては、セッションの有効期限を短く設定することで対処しましょう。

セッションにデータを保存する

現在のセッションにデータを保存するにはMojolicious::Controllerクラスのsessionメソッドを利用します。セッションIDは内部的に自動的に発行されているので、セッションIDを作成する必要はありません。セッションにデータを保存しておけば、同じユーザーからリクエストがあった場合に、保存したデータを取得することができます。

# データの保存
$c->session(name => 'Ken');
$c->session(age => 19);

# データの取得
my $name = $c->session('name');
my $age = $c->session('age');

Mojoliciousのセッションはクッキーを使って実装されており、クッキーの上限サイズが4096バイトであることに注意してください。つまりこれを超えるバイトのデータを保存することはできないということです。

Mojolicious::Liteのサンプルです。

# Mojolicious::Lite
get '/' => sub {
  my $self = shift;
  $self->session(name => 'Ken');
};

Mojoliciousのサンプルです。

# Mojolicious
package MyApp::Login;

use Mojo::Base 'Mojolicious::Controller';

sub default {
  my $self = shift;
  $self->session(name => 'Ken');
}

現在のセッションを破棄する

現在のセッションを破棄するにはMojolicious::Controllerクラスのsessionメソッドでexpiresを「1」に設定します。

$c->session(expires => 1);

Mojolicious::Liteのサンプルです。ログアウト処理の際にセッションを破棄して、「/login」というURLにリダイレクトしています。

# Mojolicious::Lite
get '/logout' => sub {
  my $self = shift;
  $self->session(expires => 1);
  $self->redirect_to('/login');
};

Mojoliciousのサンプルです。

# Mojolicious
package MyApp::Logout;

use Mojo::Base 'Mojolicious::Controller';

sub default {
  my $self = shift;
  $self->session(expires => 1);
  $self->redirect_to('/login');
}

フラッシュにデータを保存する

フラッシュにデータを保存するにはMojolicious::Controllerクラスのflashメソッドを利用します。フラッシュは、次のリクエストにだけ引き継がれるセッションのことです。次回のリクエストのためだけにデータを保存しておきたい場合、たとえばリダイレクトを行う場合になどに利用されます。

# データの保存
$c->flash(name => 'Ken');
$c->flash(age => 19);

# データの取得
my $name = $c->flash('name');
my $age = $c->flash('age');

Mojolicious::Liteのサンプルです。

# Mojolicious::Lite
get '/' => sub {
  my $self = shift;
  $self->flash(name => 'Ken');
};

Mojoliciousのサンプルです。

# Mojolicious
package MyApp::Login;

use Mojo::Base 'Mojolicious::Controller';

sub default {
  my $self = shift;
  $self->flash(name => 'Ken');
}

関連情報