CGI::Session
セッション管理をサポートする
適当に書いても動作するが、認証と組み合わせる場合、注意しないと脆弱性になる。
使い方:
// qw/-ip-match/ を付けると、同一のセッションIDが元なるIPアドレスから送信されて来たらエラーにする。
// その方が安全だが、モバイル端末を考慮する場合は、無理がある…
use CGI;
use CGI::Session [qw/-ip-match/];
// CGIオブジェクトを生成する。
my $cgi = CGI->new( );
// 既にセッションが存在している時はロードする。
// loadメソッドは、セッションが存在しない場合は空のセッションを返す。
// 空のセッションは、セッションIDを持たないので使えないが、
// $session がundefだと参照する度にチェックしなければならないので、
// 空のセッションを"セッションなし"の意味で使う方が便利。
// セッションパラメタ保存先:/tmp セッションID形式:MD5
// ここでnewを使うと、ログイン前にセッションが生成されてしまう。
// 未使用とは言え、攻撃者にセッションIDを知られるのは好ましくない。
// 攻撃者がセッションIDを調べた後、正規の利用者にそのブラウザを
// 使わせたら?→脆弱性
my $session = CGI::Session->load(undef,$cgi,{Directory=>'/tmp'});
// 必要であれば、この後認証処理を行なう。
// 認証に成功したら、改めてセッションを生成する。
// newメソッドは既にセッションが存在している場合はloadメソッドと同じ動作をするが、
// セッションが存在しない場合はセッションを生成する。
$session = CGI::Session->new(undef,$cgi,{Directory=>'/tmp'});
// セッションパラメタ$nameを$valueに設定する。
$session->param($name,$value);
// セッションパラメタ$nameを取得する。
my $value = $session->param($name);
// ハッシュリファレンス経由でセッション情報を取得する。
my $ref = $session->dataref();
my $value = $ref->{$name};
// セッションパラメタ$nameをクリアする。
$session->clear($name);
// セッションの有効期限を設定する。+30m, +2h, +1d, +1M, +1y などで表現する。
$session->expire('+30m');
// セッションを削除する。
$session->delete();
// セッションパラメタをディスクに保存する。
$session->flush();
(注)オブジェクトが破棄される時に、自動フラッシュされる事になっているが、正しくフラッシュされない場合がある。セッション情報を書き換えた時は、明示的にflushする必要がある。有効期限変更やセッションをdeleteした場合も同様。
// ブラウザにセッションIDを送る。
// $session->header を使うとSet-Cookie を出力するHTTPヘッダを作ってくれるが、
// セッションの有効期限をcookieの有効期限として出力してしまうので、
// ブラウザを閉じても、有効期限内はセッションが残ってしまう→脆弱性
// また、cookieには、httponly属性を付加するのが望ましい。
// https通信する場合は、secure属性も不可するのが望ましい。
my $cookie;
if (!$session->is_empty && !$session->is_expired) {
$cookie = $cgi->cookie(
-name => 'CGISESSID',
-value => $session->id,
-path => $path,
-httponly => 1
);
}
else {
# 削除済みor期限切れのセッションがブラウザに
# 残っていたら削除(ログアウトを実行した等)
my $sid = $cgi->cookie('CGISESSID');
if ($sid) {
$cookie = $cgi->cookie(
-name => 'CGISESSID',
-value => '',
-path => $path,
-httponly => 1,
-expires => '+5s'
);
}
}
print $cgi->header(-cookie => $cookie);
携帯HPの場合の使い方:
// クッキーが使えないのでフォーム内にIDを埋め込む。
// フォームからセッションIDを取り出す。
// この時、$sid がundefになってしまうと、CGI::Sessionは内部で
// CGI->new でCGIオブジェクトを作ってcookieを参照してしまう。
// 意図しないセッションがロードされる可能性があるので、$sidを取り出せなかった時は
// CGI::Session::ID::md5を直接呼んで新規セッションIDを生成する。
my $sid = $in{'session_id'} || CGI::Session::ID::md5->generate();
// セッションをロードする。$sidが取り出せなかった時は、空のセッションになる筈。
my $session = CGI::Session->load(undef,$sid,{Directory=>'/tmp'});
// セッションIDを取得してフォームに埋め込む
$sid = $session->id();
:
強制的に新規セッションを作る:
newやloadは、第2パラメータにundefを渡すと、内部でCGI->newを実行してCGIオブジェクトを作り、cookieを参照してしまう。
cookieが設定済みであると、既存のセッションが使われ、強制的に新規セッションを生成するスマートな方法はない。
CGI::Session::ID::md5を直接呼んで新規セッションIDを生成して第2パラメータに渡すと、強制的に新しいセッションを生成できる。
my $sid = CGI::Session::ID::md5->generate();
my $session = new CGI::Session(undef,$sid,{Directory=>'/tmp');
cookie名を変更する:
nameメソッドを使うと、cookie名を変更できるが、cookie名はCGI::Session内のグローバル変数
$CGI::Session::NAME に保存されるので、全てのインスタンスのcookie名が同時に変ってしまい、バグの元になる可能性がある。
下記の様に実装すると、CGI::Sessionの外部でcookie名を変えられる。
// セッションをロードする時
my $sid = $cgi->cookie('ANOTHER_COOKIE'};
my $session = CGI::Session->load(undef,$sid,{Directory=>'/tmp');
// セッションを生成する時
my $sid = CGI::Session::ID::md5->generate();
my $session = new CGI::Session(undef,$sid,{Directory=>'/tmp');
// cookieを出力する時
$cookie = $cgi->cookie(
-name => 'ANOTHER_COOKIE',
-value => $session->id,
-path => $path,
-httponly => 1
);
その他使いそうなメソッド:
$session->remote_addr();
最初にセッションを開いたときのREMOTE_ADDRの値を返す。
$session->is_expired();
セッション期限切れの時、trueを返す。
注)期限切れになったセッションは、再度期限を延ばしても再利用できない。
$session->is_empty();
セッション情報が何もない時(セッションIDもない。つまり、セッションがない状態でloadメソッドでインスタンスを生成した時)、trueを返す。セッション期限切れの時も、trueを返すので注意が必要。
$session->save_param($cgi [,\@list] );
$cgiのクェリーパラメータをセッション情報に保存する。@listを指定した場合は、リストに含まれる名前のパラメータのみを保存する。
このメソッドを使うとCGIモジュールが
syslogに警告を出力する。CGIモジュールを非推奨な方法で呼び出しているので使用禁止。
my $ref = $session->dataref();
セッション情報を保存しているハッシュへの参照を返す。