<?xml version='1.0' encoding='utf-8'?>
<pod xmlns="http://axkit.org/ns/2000/pod2xml">
<head>
	<title>名前</title>
</head>
<sect1>
<title>名前</title>
<para>
Tutorial - CGI::Sessionのさらに広範囲に渡って記述されたマニュアル
</para>
</sect1>
<sect1>
<title>状態メンテナンスの大要</title>
<para>
HTTPはステートレスなプロトコルですので、WEBサイトに対するそれぞれの
webサイトに対するクリックはwebサーバーによって新しい訪問として扱われます。
サーバーは直前の訪問とは無関係です。したがって全てのそれ以前のリクエスト
からの状態は失われます。このことによってショッピングカートや、
ログイン認証ルーチン、セキュリティー上の制限を設けるようなサービスなどは
web上で不可能になります。よって人々はHTTPが我々を絶望的な状況に投げ入れる
ことに対して何らかの対策を取らなければなりませんでした。
</para>
<para>
我々を救うべく、一定期間ユーザーのセッションを保つHTTPクッキーや
クエリ文字列と言った技術が誕生しました。クッキー及びクエリ文字列だけでは
<strong>RFC 2965, Sesctoin 5, &quot;Implementation Limitations&quot;</strong>に記述されている通り
我々を救うにはほど遠いものがあり、いくつか別のライブラリ/技術がさらなる
機能とさらなる信頼性、及び継続性を保つシステムを約束するために
開発されています。CGI::Sessionはそれらの中の一つです。
</para>
<para>
このライブラリを議論する前に、代わりの解決方法を見てみることにしましょう。
</para>
<sect2>
<title>クッキー</title>
<para>
クッキーはwebサーバーに権利が与えられたユーザーのハードディスクに配置される
テキスト情報の断片であり、ユーザーエージェント(例えばWeb Browser)に
特化した部分で互換性があると仮定します。クッキーが配置されると、ユーザー
エージェントはサーバーアプリケーション(CGI)は同一のユーザーエージェン
トによる過去のリクエストに関連した手段を持つことができます。したがって
HTTPのステートレスと言う特性に打ち勝ったと言えるのです。
</para>
<para>
しかしながら、クッキーはHTTPのステートレスと言う特性に対する解決を約束す
るものであるような気がするものの、ドメインやユーザーエージェント数毎のクッキー
数の制限やクッキーごとのサイズと言った制限が存在します。ユーザーエージェ
ントは少なくとも一度に300個、ドメインごとに20個でかつクッキーごとに
4096バイトの格納を許容することが求められます。
それらの事項に対し、いくつかのプライバシーおよびセキュリティに関する
懸念が浮かびあがります。それらの懸念は<strong>RFC 2965</strong>における6-<strong>&quot;PRIVACY&quot;</strong>および
7-<strong>&quot;Security Considerations&quot;</strong>の各節にリストされているのを見付けることが
できるでしょう。
</para>
</sect2>
<sect2>
<title>クエリ文字列</title>
<para>
クエリ文字列とはこのようなURLのクエスチョンマーク(?)に続く文字列です。
</para>
<verbatim><![CDATA[
http://my.dot.com/login.cgi?user=sherzodr&password=topSecret
]]></verbatim>
<para>
おそらくあなたは既にこの解法を使っていたのではないかと思われます。
クエリ文字列でも別の場所へのクリックによって状態を渡す助けになりますが、
あなたはどれだけ安全なものかと言うことを考えていますか？これらのURLが
大半のユーザーエージェントによって誰もがアクセスできるサーバーのアクセスログに
残るケースが頻繁であることを念頭に置くと、安全であるとは言い難いと言えます。
</para>
</sect2>
<sect2>
<title>隠しフィールド</title>
<para>
隠しフィールドはクエリ文字列の代わりに使用されるもう一つの選択肢であり、
隠しフィールドはPOSTメソッド及びGETメソッドの両方で用いられると言う
特性があります。GETメソッドで用いられる隠しフィールドは一度サブミットされると
本物のクエリ文字列に返信してしまうため、クエリ文字列を送信するのと比べて
何の進歩もありません。しかしながらPOSTリクエストは姉貴分であるGETのような
制限が無いのですが、それらを持つページをブラウザによってキャッシュさせる
ことができ、(もちろん)ページのソースコードの範囲で有効になってしまいます。
多数のステート情報を記憶させておくとなる際にも扱いにくくなります。
(例えばショッピングカートや優れたサーチエンジンです。)
</para>
<para>
クエリ文字列及び隠しフィールドはブラウザを閉じるか、さもなくばブラウザの
&quot;戻る&quot;ボタンをクリックすることで簡単に失われてしまいます。
</para>
</sect2>
<sect2>
<title>サーバーサイドセッション管理</title>
<para>
このテクニックは前述した技術にサーバーサイドでのストレージデバイスを
プラスしたものを基に構築されており、状態のデータを特定のセッションとして
保存します。それぞれのセッションはサーバー内のデータによって連想された
ユニークなidを所持しています。このidはユーザーエージェンと側でクッキーや、
クエリ文字列や隠しフィールド、又はその全てを同時に行う言ういずれかの形でも
連想されます。
</para>
<para>
進展した箇所:
</para>
<list>
<item><para>
私達はもはやクッキーの量やサイズと言ったユーザーエージェントの制限に
依存する必要が無くなったのです。
</para>
</item>
<item><para>
ユーザーのユーザー名やEメールアドレス、嗜好のようなきわどいデータ類は
もうそれぞれのリクエストごとにネットワーク上を行き来する必要がなくなったの
です。(クエリ文字列、クッキー及び隠しフィールドに対してのことを言っています)。
ネットワーク上を行き来するのはセッションのために生成されたユニークなidのみで
(例えば&quot;ID-1234&quot;です)、例えどんな悪意をもつ者にとっても意味をなさないはずです。
</para>
</item>
<item><para>
ユーザーはセキュアでない平文型式のデータ(クッキーファイルのことです。)が
コンピュータ内に保存されていることに神経を尖らせる必要が無くなるのです。
</para>
</item>
<item><para>
巨大で複雑でさえもある(メモリ内の)データ構造を透過的に扱うことが可能と
なります。
</para>
</item>
</list>
<para>
これがCGI::Sessionに関する全てです - サーバーサイドセッション管理の実装。
今こそそのぬかるみから足を踏み出すチャンスなのです。
</para>
</sect2>
</sect1>
<sect1>
<title>プログラミングスタイル</title>
<para>
もしあなたがサーバーサイドセッション管理システムを扱ったことが無い場合、
それが凄まじく入り組んだ物のように思えるのかもしれません。好運なことに、
CGI::Sessionにおけるこの扱いにくい処理はエレガントな手法によって、
ライブラリを用いることで全ての複雑な箇所を透過的に扱うことを可能にするのです。
マニュアルのこの節ではセッション管理の背後にあるロジックと、CGI::Sessionの
プログラミングスタイルについての両方の導入チュートリアルとして扱ってもらって
不遜はありません。
</para>
</sect1>
<sect1>
<title>最初にあなたが知るべき事</title>
<para>
CGI::Session 3.xの文法は以前のリリースから変貌を遂げました。しかし我々はまだ
前バージョンと互換を取るために、継続して古い文法をサポートし続けています。
そのため、3.xインターフェースの初期のリリースに関して我々は'-api3'スイッチを
導入しました:
</para>
<verbatim><![CDATA[
use CGI::Session qw/-api3/;
]]></verbatim>
<para>
これによってライブラリに新しい文法を使うことを伝えるのです。しかしライブラリの
最新リリースでは、それを自動的に探知するでしょう。つまり&quot;-api3&quot;スイッチをもはや
呼び出す必要が無くなるのです。あなたが古い2.xのapiをもう使わないというならば、
我々にそのことを尋ねるのはどうかお止め下さい。我々はあなたに何も教えない
でしょう。
</para>
<para>
しかし本ライブラリを使い始める前に、どこにどうやってセッションデータを
ディスクに保存するのかを決断する必要があるでしょう。言い替えればどのドライバ
を使うのかを伝えなければなりません。あなたはデフォルトでディストリビュー
ションに搭載されている&quot;File&quot;, &quot;DB_File&quot;及び&quot;MySQL&quot;ドライバのいずれかを選択
することが可能です。このドキュメントでは要求するものがもっとも少ないため
理解しやすい&quot;File&quot;ドライバを用いた例を示します。実行するには次のように
セッションオブジェクトを作成して下さい。
</para>
<verbatim><![CDATA[
use CGI::Session;
$session = new CGI::Session("driver:File", undef, {Directory=>'/tmp'});
]]></verbatim>
<para>
一つ目の引数は呼び出されるデータソース(略してDSN)の名前です。undefであれば、
ライブラリはデフォルトのドライバ、「File」を使用するでしょう。よって、
下記の例のようにドライバに関しては明示的に示す代わりにこのように表現する
ことも可能です:
</para>
<verbatim><![CDATA[
$session = new CGI::Session(undef, undef, {Directory=>'/tmp'});
]]></verbatim>
<para>
これにより、デフォルトの設定になることが保証されます。
</para>
<para>
２つ目の引数は初期化用のセッションidです。もしundefであれば、CGI::Sessionに
新しいセッションを強制的に作らせます。session idを渡す代わりに、CGI.pm
オブジェクトを渡すか、さもなくばcookie()あるいはparam()メソッドのいずれかを
実装したオブジェクトを渡すことも可能です。このケースでは、ライブラリは
<strong>CGISESSID</strong>クッキーあるいは<strong>CGISESSID</strong>CGIパラメータ(クエリ文字列)の
いずれかよりセッションidを取得しようと試みるでしょう。
</para>
<para>
３番目の引数はハッシュのリファレンスと言う形であるべきです。これは特定の
CGI::Sessionドライバにのみ適用されます。使用可能な全属性の一覧を見たい場合は
それぞれのCGI::Sessionドライバを参照願います。
</para>
<list>
<item><para>
<link xref='CGI::Session::File'>File</link> - 平文でセッションデータを格納するためのデフォルトの
ドライバです。フルネーム: <strong>CGI::Session::File</strong>
</para>
</item>
<item><para>
<link xref='CGI::Session::DB_File'>DB_File</link> - BerkelyDBにセッションデータを格納します。
必要物: <link xref='DB_File'>DB_File</link>。フルネーム: <strong>CGI::Session::DB_File</strong>
</para>
</item>
<item><para>
<link xref='CGI::Session::MySQL'>MySQL</link> - MySQLテーブルにセッションを格納します。
必要物<link xref='DBI'>DBI</link>及び<link xref='DBD::mysql'>DBD::mysql</link>。フルネーム: <strong>CGI::Session::MySQL</strong>
</para>
</item>
</list>
<para>
Note: あなたは本ライブラリのためにドライバを自身で書くことも可能です。
このマニュアルの詳細の各節を隅々まで御覧下さい。
</para>
</sect1>
<sect1>
<title>新しいセッションの作成</title>
<para>
ユーザーに対する新しいセッションと言う焼き印を生成するためには、コンストラクタ
- new()に未定義の値を２つ目の引数に渡すだけで構いません。
</para>
<verbatim><![CDATA[
$session = new CGI::Session("driver:File", undef, {Directory=>"/tmp"});
]]></verbatim>
<para>
ディレクトリはセッション分散したファイルの形で格納されるであろうファイル及び
そのロックの場所を示します。セッションオブジェクトを生成する際には
私が上記に示したことをするために、次の手順が踏まれるでしょう:
</para>
<list>
<item><itemtext>1</itemtext>
<para>
あなたのためにセッションIDは生成され
</para>
</item>
<item><itemtext>2</itemtext>
<para>
あなたが指定したディレクトリ中からidによって格納されたファイルが連想されます。
</para>
</item>
</list>
<para>
これからは、あなたが新しく生成されたセッションidにアクセスしたい場合は
ただこうすれば良いのです。
</para>
<verbatim><![CDATA[
$sid = $session->id();
]]></verbatim>
<para>
クッキーとして送信される、あるいはクエリ文字列やフォーム内の隠しフィールドと
してそのまま用いられることが可能な<strong>a983c8302e7a678a2e53c65e8bd3316</strong>と言った
文字列を返します。
</para>
<verbatim><![CDATA[
$cookie = $cgi->cookie(CGISESSID => $session->id);
print $cgi->header( -cookie=>$cookie );
]]></verbatim>
<para>
もし上記の方法が無理であれば、<link xref='CGI'>CGI</link>の詳細を御覧下さい。
</para>
<sect2>
<title>既存のセッションを初期化する</title>
<para>
ユーザーが他のリンクやちょっと間を置いてサイトを再訪問した際、我々は再び
新しいセッションを作成しなくてはならないのでしょうか？もちろん違います。
これではセッションの維持と言う目的を打ち負かしてしまうことになるのですから。
我々がクッキーとして既にidを送信していたら、我々が皆必要とするものは
セッションオブジェクトが生成されている間２つ目の引数としてそのidを渡すこと
のみです。
</para>
<verbatim><![CDATA[
$sid = $cgi->cookie("CGISESSID") || undef;
$session    = new CGI::Session(undef, $sid, {Directory=>'/tmp'});
]]></verbatim>
<para>
下記の文法ではまずはじめに既存のセッションデータを初期化しようとし、もし
新しいセッションファイルを生成する場合(セッションが存在しない場合)でも、
我々が意図した結果となります。しかし、もしユーザーがクッキーをサポート
していない場合はどうなるのでしょうか？その場合我々はクエリ文字列として全ての
urlに追加する必要があります。クッキーを追加する方法を御覧下さい:
</para>
<verbatim><![CDATA[
$sid = $cgi->cookie('CGISESSID') || $cgi->param('CGISESSID') || undef;
$session = new CGI::Session(undef, $sid, {Directory=>'/tmp'});
]]></verbatim>
<para>
あなたがCGIオブジェクトの扱いに長けていると仮定して、上記の２行を１行に
納めることが可能です:
</para>
<verbatim><![CDATA[
$session = new CGI::Session(undef, $cgi, {Directory=>"/tmp"});
]]></verbatim>
<para>
もし２つ目の引数として文字列の代わりにオブジェクトを渡すと、CGI::Sessionは
クッキーあるいはクエリ文字列からセッションidを取得しようと試み、適宣に
セッションを初期化します。クッキー及びクエリ文字列のパラメータはデフォルトで
<strong>CGISESSID</strong>であると仮定します。この設定を変更するためには、CGI::Session
あるいはそのオブジェクトのうちいずれかの<code>name()</code>クラスメソッドを呼び出す
必要があるでしょう:
</para>
<verbatim><![CDATA[
CGI::Session->name("MY_SID");
# 又は
]]></verbatim>
<verbatim><![CDATA[
$session->name("MY_SID");
]]></verbatim>
<verbatim><![CDATA[
$session = new CGI::Session(undef, $cgi, {Directory=>'/tmp'});
]]></verbatim>
</sect2>
<sect2>
<title>セッションにデータを格納する</title>
<para>
<code>param()</code>メソッドを用いてオブジェクト内に値を一つ格納するためには:
</para>
<verbatim><![CDATA[
$session->param("my_name", $name);
]]></verbatim>
<para>
あなたは配列や、ハッシュやオブジェクトなどのような複雑なデータを格納するために
<code>param()</code>メソッドを用いることが可能です。配列及びハッシュを格納する際は、
リファレンスとして渡さなければなりません:
</para>
<verbatim><![CDATA[
@my_array = ("apple", "grapes", "melon", "casaba");
$session->param("fruits", \@my_array);
]]></verbatim>
<para>
同様にオブジェクトも格納することが可能です:
</para>
<verbatim><![CDATA[
$session->param("cgi", $cgi);   # CGI.pmオブジェクトを格納します
]]></verbatim>
<para>
あなたが望むセッションオブジェクト内にCGIのパラメータを全て格納する方法も
存在します。あなたはセッションオブジェクトに各フォームの要素からの
あなたは後で多数のクエリパラメータを格納しなければならないためににこの形式を
望むことだと思われます。以下の文法を御覧下さい:
</para>
<verbatim><![CDATA[
$session->save_param($cgi, ["keyword", "category", "author", "orderby"]);
]]></verbatim>
<para>
save_param()は上記の全CGIパラメータをセッションオブジェクトに格納させる
でしょう。これは以下と同様のことを言っています。
</para>
<verbatim><![CDATA[
$session->param("keyword",  $cgi->param("keyword"));
$session->param("category", $cgi->param("category"));
# など全てのフォーム要素に対して
]]></verbatim>
<para>
全CGIパラメータを保存したい場合ですが、<code>save_param()</code>の２つ目の引数を省略
するだけです:
</para>
<verbatim><![CDATA[
$session->save_param($cgi);
]]></verbatim>
<para>
上記の文法は全ての使用可能/アクセス可能なCGIパラメータを保存します。
</para>
</sect2>
<sect2>
<title>保存されたデータへのアクセス</title>
<para>
もしあなたがデータにアクセスできないならば、保存されたデータに接点がないの
です。あなたは一度データを保存するのに用いた同じ<code>param()</code>で保存されたデータに
アクセスすることが可能です。
</para>
<verbatim><![CDATA[
$name = $session->param("my_name");
]]></verbatim>
<para>
上記のparam()の形式では&quot;my_name&quot;として過去に保存されたセッションのパラメータを
取得します。過去に保存された@my_arrayを取り出すには:
</para>
<verbatim><![CDATA[
$my_array = $session->param("fruits");
]]></verbatim>
<para>
配列へのリファレンスが戻って来るでしょう。そして、@{$my_array}として参照する
事が可能です。
</para>
<para>
あなたはユーザーの好みや以前のアクションに応じた入力済、あるいは選択済の
ラジオボタンやチェックボックス、ドロップダウンメニューなどのフォームを
とても頻繁に作らなくてはならないだろうことにお気づきであると思われます。
textやtextareasならそれほど大したことではありません: 単に一つのパラメータを
セッションから取得し、textフィールドにハードコーディングすれば良いのですから。」しかしラジオボタンやチェックボタン、スクロールするリストのグループを
扱うとき、あなたはどうするでしょう？この目的のため、CGI::Sessionはload_param()
メソッドを提供しており、CGIオブジェクトに与えられたセッションパラメータを
読み出します (save_param()メソッドあるいはその他の選択肢で過去に保存されている
と仮定して):
</para>
<verbatim><![CDATA[
$session->load_param($cgi, ["fruits"]);
]]></verbatim>
<para>
さて、CGI.pmを使って選択済のチェックボックスを生成するには:
</para>
<verbatim><![CDATA[
print $cgi->checkbox_group(fruits=>['apple', 'banana', 'appricot']);
]]></verbatim>
<para>
もしあなたがHTML::Templateを用いることでスキンからコードを分離させる場合、
HTML::Templateを用いてCGI::Sessionオブジェクトをassociateすることによって
同様のことが可能になります。我々はこのトリックが大好きです。
</para>
<verbatim><![CDATA[
$template = new HTML::Template(filename=>"some.tmpl", associate=>$session);
print $template->output();
]]></verbatim>
<para>
HTML::Templateでassociateする間、セッションオブジェクトに&quot;first_name&quot;及び
&quot;email&quot;パラメータが格納されていると仮定すると、&quot;some.tmpl&quot;ファイルを用いて
それらの値にアクセスできます:
</para>
<verbatim><![CDATA[
Hello <a href="mailto:<TMPL_VAR email>"> <TMPL_VAR first_name> </a>!
]]></verbatim>
<para>
HTML::Templateによるさらなるトリックを知りたい場合はライブラリマニュアル
(<link xref='HTML::Template'>HTML::Template</link>及び<link xref='CGI::Session::CookBook'>CGI Session CookBook</link>。
</para>
</sect2>
<sect2>
<title>セッションを閉じる</title>
<para>
通常あなたは明示的にセッションを閉じる必要はありません。プログラムが終了する
か、あるいはセッションオブジェクトがスコープからはずれた際、セッションが閉じ
られます。しかしながら時にはCGI::Sessionの&lt;close()&gt;メソッドあるいはオブジェ
クトを未定義にすることによってセッションを明示的に閉じたい場合があるかもしれ
ません。セッションを閉じるとは一体全体何なのでしょう？ - あなたは尋ねるでしょ
う。セッションがアクティブな間、ディスク内のセッションオブジェクトの更新が
すぐに行われるわけではありません。<code>flush()</code>メソッドでバッファをフラッシュさ
せると言う選択肢をとるかあるいはプログラムを終了させるかあるいはclose()メソッ
ドを明示的に呼び出してセッションを破棄するまではメモリ内に保存されます。
</para>
<para>
状況によってはセッションを閉じたいもののプロセスをしばらくの間終了させたくない
場合もあるかと思われます。おそらくそのようなケースはGUI及びデーモンアプリケー
ションによるものであると思われます。この場合close()こそがあなたが求めていたもの
なのです。Note: 我々はclose()よりもシンプルな開放用のメソッドを好みます。
(close()は機能面で劣ります。)
</para>
<verbatim><![CDATA[
undef($session);
]]></verbatim>
<para>
もしあなたがセッションをオブジェクトを何らかの理由でバッファ内のデータと
ディスク内のデータを同期させたいならば、<code>flush()</code>こそあなたの求めるものです。
</para>
<para>
Note: close()も同様にflush()を呼び出します。したがってclose()を呼び出す前に
flush()を呼び出す必要は全く無いのです。
</para>
</sect2>
<sect2>
<title>セッションデータのクリア</title>
<para>
セッションデータを保存し、セッションデータにアクセスすると、ある時点で
全てでなくとも、特定のセッションデータをクリアしたくなるでしょう。この目的の
ためにCGI::Sessionはセッションパラメータをオブジェクトから消すべきパラメータ
名を指示した配列のリファレンスの表現で引数を一つ取る<code>clear()</code>メソッド
をオプショナルで提供しています。
</para>
<verbatim><![CDATA[
$session->clear(["~logged-in", "email"]);
]]></verbatim>
<para>
上記の行ではセッションパラメータ&quot;~logged-in&quot;及び&quot;email&quot;をセッションから消去し
ます。そして次にこうすると:
</para>
<verbatim><![CDATA[
$email = $session->param("email");
]]></verbatim>
<para>
iundefが戻ります。もし<code>clear()</code>の引数を省略した場合、あなたが今までにセッショ
ンオブジェクトに格納したセッションパラメータが削除されてしまうのでお気を付け
下さい。セッションそのものを消してしまうのではないことを念頭に置いて下さい。
セッションはまだ開いていてアクセス可能です。中に格納されたパラメータが消去
されただけなのです。
</para>
</sect2>
<sect2>
<title>セッションを削除する</title>
<para>
もし始まりが存在し、終わりが存在するならば。もしセッションが作らるなら
ば、それはディスクから削除することが可能である方が良いに決まっています。
</para>
<verbatim><![CDATA[
$session->delete();
]]></verbatim>
<para>
上記のように<code>delete()</code>を呼び出すことによってディスクからセッションを削除
した方が好ましいと言えます。<code>clear()</code>と混同しないように、これは特定のセッシ
ョンパラメータをクリアしますが、セッションは開いたままです。
</para>
</sect2>
<sect2>
<title>期限切れ</title>
<para>
CGI::Sessionはセッションデータの期限切れを実現するための限られた機能を提供
します。セッションの期限切れはdelete()経由で消去することと同等ですが、削除は
自動的に行われます。セッションを期限切れにするためには、ライブラリに最終アク
セス時からどれだけ長く有効であるかを教える必要があります。時間が来れば、
CGI::Sessionはセッションを取得することを拒否します。そのことによりセッション
は削除され、真新しいセッションが返って来ます。セッションに対する期限切れを
示す時計を割り当てるためにはexpire()メソッドを用います。
</para>
<verbatim><![CDATA[
$session->expire(3600);     # 3600秒後に期限切れになります
$session->expire('+1h');    # 1時間後に期限切れになります
$session->expire('+15m');   # 15分後に期限切れになります
$session->expire('+1M');    # 1ヵ月後に期限切れになります
]]></verbatim>
<para>
しかし時には、セッション全体に対する有効期限の代わりに完全に特定パラメータ
のみに限定した有効期限を設定することもできます。作者はログイン成功後に
&quot;_logged_in&quot;フラグを真に設定し、30分間と言ったある有効期限をフラグに割り当てる
ようなログイン/認証可能なサイトでこのことは大抵行われています。これは30分間
アイドルした後、CGI::Sessionが&quot;_logged_in&quot;フラグをclear()し、もう一度ユーザー
はログインしなおさなくてはならないことを示しています。私は単にexpiring()を
セッションそのものによって成し遂げられ得るのと同じであることを認めますが、
これらの方法ではその他のセッションパラメータが失われるでしょう。ショッピング
カートなどのセッションではこちらの方が好ましいと言えます。
</para>
<para>
この形式では個人のプロフィール情報に対するログインに成功してから10時間まで
放っておいてもアクセスし続けることが可能ですが、10分間放っておくとクレジット
カードへのアクセスの期限が切れると言った、階層化されたセキュリティ認証をシミ
ュレートするのに用いることも可能です。
</para>
<verbatim><![CDATA[
$session->expire(_profile_access, '+10h');
$session->expire(_cc_access, '+10m');
]]></verbatim>
<para>
上記の文法によって5時間放置したとしても、まだ個人情報にはアクセス
できます。しかし、クレジットカード情報にアクセス、あるいは更新しようとしても、
おそらく「もう一度ログインして下さい」とスクリーンに表示されることでしょう。
</para>
<para>
このことによりCGI::Sessionのプログラミングスタイルの議論に対する
ひとまずの結論を下します(少なくとも新しくライブラリをリリースするには
至りました)。
マニュアルの最後にはドライバを実装する、あるいはライブラリの構造について理解
したい方々のために<link xref='#セキュリティ'>&quot;セキュリティ&quot;</link>問題及び<link xref='#ドライバの特性'>&quot;ドライバの特性&quot;</link>をカバーしてい
ます。
</para>
</sect2>
</sect1>
<sect1>
<title>セキュリティ</title>
<para>
「どうやったらCGI::Sessionをセキュアに使えるの？」、「もしセッションユーザーの
セッションidを取られてしまったら他の人が別のブラウザを使ってセッションを
乗っ取ることができてしまうの？」、「セッションidは推測可能なの？」と言った
私自身何度も何度も答えていることに気づいた質問群です。
</para>
<sect2>
<title>格納</title>
<para>
ライブラリのセキュリティは実装に依存する局面が多いと言えます。このライブラリを
利用した後は、あなたはもうセッションidに関するユーザーのクッキーに対する情報を
送らなくても良いのです。しかし、あなたはまだサーバー側にデータを保存しなくては
なりません。よってもう一つの疑問が挙がって来ます。それは悪意のある人が
サーバー内のセッションデータにアクセスできやしないか、さらに想定外のデータが
含まれるセッションデータを作れやしないか、そしてさらにセッションを作った人に
対する情報が不正流用されやしないかと言うことです。いいですか、その答えはそれ
を実装するあなたによるのです。
</para>
<para>
最初のルールの概要ですが、セッションの中にはパスワードやその他きわどいデータを
保存しないことです。もしあなたが自分でこれが重要であると認識したらば、きっと
悪の目玉はサーバー内のセッションファイルにアクセスすることもないでしょう。もし
あなたがMySQLのようなRDBMSドライバを使っているのであれば、データベースは
ユーザー名/パスワードのペアで保護されているでしょう。しかしもしそれが平文の
形でファイルシステムに保存されているとしても、きっとあなたを除く誰しもがそれ
らのファイルへアクセスできないでしょう。
</para>
<para>
デフォルトのドライバの設定ではディスク内に保存することを可能にするために
データをシリアライズするData::Dumperクラスを使用させることになります。
Data::Dumperの出力結果は人間が可読であるデータ構造なのですが、
このデータをオープンすればあなたが解読することができるのです。
もしStorableか又はFreezeThawのいずれかをシリアライザとして用いるために、
セッションオブジェクトを設定した場合は悪意のある者に対抗してセッション
データとは無関係で意味なさないような複雑なデータを生成するでしょう。
しかしこのことをセキュリティのための警戒として用いてはなりません。
悪の指はセッションファイルを復号するStorableあるいはFreezeThawを用いた
高速なセッションデータ復元プログラムをたやすく書いてしまうのです。
</para>
<para>
セッションファイルの中身の更新に関して気を病むのも良くないことです。も
ちろん、CGI::Sessionを用いる限りそのようなことは起こりませんが、
心配したところで別段害があるわけでもありません。
</para>
<para>
貴重なデータを伴うセッションを長期間保持してはなりません。このことにより、
悪意のある者が時間内に誰かの有効なセッションidを盗む可能性が増大します
(なんらかの方法で知って)。
</para>
<para>
常に&quot;-ip-match&quot;スイッチを使いなさい！！！
</para>
<para>
詳細は&quot;-ip-match&quot;にあるので読みましょう。
</para>
</sect2>
<sect2>
<title>SESSION ID</title>
<para>
セッションidが簡単に推測されることはありません(Incr Idジェネレータを用いない
限りは)!CGI::Sessionにおけるデフォルトの設定ではDigest::MD5はプロセスid、
エポックからの秒数およびランダムな数字を用いて32文字長のダイジェストを
生成します。しかしながらこの文字列は他人が推測することは不可能で、もし
何らかの方法で見付けることができたとしても、他人の識別子を使うことなど
できるのでしょうか？
</para>
<para>
あなたはただ誰かにemailかインスタントメッセージ経由でオンラインアカウントの
プロファイルへのリンクを提供するところで現在ログインしていると言う
シナリオを考えてみましょう。あなたがその人に提供するURLはクエリ文字列の一部
としてセッションidを含んでいます。もしそのサイトがセッションを初期化して、
ただクエリ文字列のパラメータを用い、今サイトに現れた人があなたであることを
示すリンクをクリックすると、ただちにあなたの機密データの全てにアクセス
したかもしれません。なんと恐ろしく無知な実装なのでしょう。セッションidを
伴ったURLを貼り付けることが、事故が起こるのを待つようなものであることを
知らないなんと哀れなお子様なのでしょう。
</para>
<para>
もしあなたが単にクッキーをセッションidの転送役として用いた場合でさえも、
同じクッキーファイルの中に同じセッションidを伴うクッキーを植え込み、webブラウザ
に適切なセッションidをサーバーに送信するよう細工することは難しいことでは
ないのです。よって、もし私達にセッションデータを取得するよう依頼している人が
実際にセッションデータを最初に初期化した人であるかどうか、セキュリティ用の
キーをチェックすべきです。CGI::Sessionではライブラリを「使って」いる間、
&quot;-ip_match&quot;スイッチを使用加納にすることによってこのようなケースにあなたが注意を
注ぐことを手助けします。
</para>
<verbatim><![CDATA[
use CGI::Session qw/-ip-match/;
]]></verbatim>
<para>
代わりの手段として、$CGI::Session::IP_MATCHに1と言った真を示す値を設定
する方法があります。このことを行うことによって、セッションのデータ格
納に備えた初期化の前に、セッション中に格納されたipアドレスとユーザーが
該当セッションに対して尋ねた際のipアドレスとが一致するか否かを検証し
ます。ライブラリがセッションを返すようなケースでは、一致しなかった場合
適切なエラーメッセージとともにdieします。
</para>
</sect2>
</sect1>
<sect1>
<title>ドライバに特化した事項</title>
<para>
この節は本ライブラリに対する独自の格納メカニズムを実装したいドライバの作者の
ためのものです。同様にがらくたのサブクラス化を楽しむ人々はこの節が便利なもの
であると気づくべきです。をここで我々はCGI::Sessionおよびそのドライバの構造を議論します。
</para>
<sect2>
<title>ライブラリの大要</title>
<para>
ライブラリは<link xref='メソッド'>メソッド</link>節にリストされた基本メソッドを提供しています。
CGI::Sessionが自ら進んで提供しないメソッドはセッションデータを書き出す処理、
ディスクからセッションデータを取得する処理、そしてデータを消去する処理を
必要とするメソッドのみです。これらはドライバに特化したメソッドであるため、
それらが所属するべきところにあるのです。
</para>
<para>
言い替えるならば、ドライバはCGI::Sessionを基底クラスとして用いたもう一つの
Perlライブラリであり、ディスクアクセスの処理を行ういくつかの付加的なメソッド
を提供します。
</para>
</sect2>
<sect2>
<title>シリアライズ</title>
<para>
ドライバの使用の話に入る前に、データがどうやって格納されるかについてお話しして
おきましょう。flush()が呼び出されるか、さもなくばプログラムが終了した際に、
CGI::Sessionはディスクのどこに格納するのかを尋ね、そしてハッシュのリファレンス
の形でデータが渡されます。それからデータをディスクに格納するデータをシリアラ
イズ化するのはドライバの責任です。
</para>
<para>
あなたはドライバに独自のシリアライズ化の実装を組み込んでも構わないのですが、
CGI::Sessionのディストリビューションではあなたが継承し、そのオブジェクトにおけ
るデータをシリアライズ化し、格納するためのfreeze()メソッドを呼び出すことが
できるいくつかのライブラリを提供しています:
</para>
<list>
<item><itemtext><link xref='CGI::Session::Serialize::Default'>CGI::Session::Serialize::Default</link></itemtext>
</item>
<item><itemtext><link xref='CGI::Session::Serialize::Storable'>CGI::Session::Serialize::Storable</link></itemtext>
</item>
<item><itemtext><link xref='CGI::Session::Serialize::FreezeThaw'>CGI::Session::Serialize::FreezeThaw</link></itemtext>
</item>
</list>
<para>
Example:
</para>
<para>
例:
</para>
<verbatim><![CDATA[
# $dataは格納される必要があるハッシュのリファレンスです。
]]></verbatim>
<verbatim><![CDATA[
my $storable_data = $self->freeze($data)
]]></verbatim>
<para>
今、$storable_dataはディスクへ安全に保存することができます。
</para>
<para>
ドライバがディスクからデータを取得するために尋ねられる際には、シリアライズ化
されたデータはデシリアライズされるべきです。前述のシリアライザはthaw()メソッド
も提供しており、１番目の引数としてデータをシリアライズ化されたデータを渡すと
保存される前の形でPerlのデータ構造を返します。例:
</para>
<verbatim><![CDATA[
my $hashref =  $self->thaw($stored_data);
]]></verbatim>
</sect2>
<sect2>
<title>ドライバのメソッド</title>
<para>
ドライバはもう一つのPerlライブラリで、CGI::Sessionを基底クラスとして用い、
以下のメソッドを提供することが要求されます:
</para>
<list>
<item><itemtext><code>retrieve($self, $sid, $options)</code></itemtext>
<para>
retrieve()はディスクからデータを取得する際に上記3つの引数を伴いCGI::Sessionに
よって呼び出されます。$selfはセッションオブジェクトで、$sidはセッションid、
そして$optionsはハッシュのリファレンスの形で引数の一覧をnew()に渡すのです。
メソッドはアンシリアライズ化されたセッションデータを返すか、さもなくば失敗を
示すundefを返すべきでし。もしエラーが発生した場合、die()を呼んだりcroak()を
呼ぶ代わりにerror()にエラーメッセージをセットし、undefを返すことを提案
します:
</para>
<verbatim><![CDATA[
unless ( sysopen(FH, $options->{FileName}, O_RDONLY) ) {
    $self->error("Couldn't read from $options->{FileName}: $!");
    return undef;
}
]]></verbatim>
<para>
もしそのドライバが存在しないセッションを要求した場合は、なんらかのエラーメッセージを生成するべきではなく、単純にundefを返すべきです。このことによってCGI::Sessionが新たなセッションidを作ることを通知することができるでしょう。
</para>
</item>
<item><itemtext><code>store($self, $sid, $options, $data)</code></itemtext>
<para>
store()はセッションデータが格納されることが必要になった際CGI::Sessionによって
呼び出されます。格納されるためのデータは3つ目の引数としてメソッドに
渡されます。その引数はハッシュへのリファレンスです。成功を示す真の値
返すか、それ以外のケースではundefを返すべきです。エラーメッセージはerror()に
渡されるべきです。
</para>
</item>
<item><itemtext><code>remove($self, $sid, $options)</code></itemtext>
<para>
CGI::Sessionがdelete()メソッド経由でディスクからセッションデータを消去するよう
要求された際、remove()は呼び出されます。成功の場合真を示す値を、それ以外の時は
undefを返し、エラーメッセージはerror()に設定されるべきです。
</para>
</item>
<item><itemtext><code>teardown($self, $sid, $options)</code></itemtext>
<para>
セッションオブジェクトが破棄されようとしている際にclose()経由で明示的に、
さもなくばプログラム中断時に暗黙的に呼び出されます。
</para>
</item>
</list>
</sect2>
<sect2>
<title>IDの生成</title>
<para>
CGI::Sessionはgドライバにenerate_id()メソッドを提供することも要求し、それは
新たなセッションのためのidを返します。繰り返しますが、自分自身で車輪を再発明
することは歓迎されます。しかし、覚えておいてほしいのですが、CGI::Sessionの
ディストリビューションには対となる２つのgenerate_id()を提供するライブラリが
含まれます。あなたは単純にそれらを継承すべきです。以下に挙げるIDジェネレータ
が使用可能です:
</para>
<list>
<item><itemtext><link xref='CGI::Session::ID::MD5'>CGI::Session::ID::MD5</link></itemtext>
</item>
<item><itemtext><link xref='CGI::Session::ID::Incr'>CGI::Session::ID::Incr</link></itemtext>
</item>
</list>
<para>
さらなる詳細を知るには各マニュアルを御参照下さい。
</para>
<para>
あなた独自のスタイルのidを用いたい場合、あなたはgenerate_id()メソッドを
上記ライブラリの継承を行うこと無しに明示的に定義することが可能です。
もしくはあなた独自の<strong>CGI::Session::ID::YourID</strong>ライブラリを書き、
session idを返す&quot;generate_id()&quot;メソッドを簡潔に定義し、それから
DSNを構成する１部としてコンストラクタにその名前を与えて下さい。
</para>
<verbatim><![CDATA[
$session = new CGI::Session("id:YourID", undef, {Neccessary=>Attributes});
]]></verbatim>
</sect2>
<sect2>
<title>設計図</title>
<para>
あなたのCGI::Sessionディストリビューションは
ともにあなたのドライバのための出発点として使用される
Session/Blueprint.pmファイルともに配付されることになります。
</para>
<verbatim><![CDATA[
package CGI::Session::BluePrint;
]]></verbatim>
<verbatim><![CDATA[
use strict;
use base qw(
    CGI::Session
    CGI::Session::ID::MD5
    CGI::Session::Serialize::Default
);
]]></verbatim>
<verbatim><![CDATA[
# これより下で必要なライブラリをロードします
]]></verbatim>
<verbatim><![CDATA[
use vars qw($VERSION);
]]></verbatim>
<verbatim><![CDATA[
$VERSION = '0.1';
]]></verbatim>
<verbatim><![CDATA[
sub store {
    my ($self, $sid, $options, $data) = @_;
]]></verbatim>
<verbatim><![CDATA[
my $storable_data = $self->freeze($data);
]]></verbatim>
<verbatim><![CDATA[
#ここであなたは$storable_dataをディスクに格納する必要があります
]]></verbatim>
<verbatim><![CDATA[
}
]]></verbatim>
<verbatim><![CDATA[
sub retrieve {
    my ($self, $sid, $options) = @_;
]]></verbatim>
<verbatim><![CDATA[
# あなたは格納されたデータを取得する必要があるでしょう。
# そして$self->thaw()メソッドを用いてシリアライズ化されたデー
# タを復元します
]]></verbatim>
<verbatim><![CDATA[
}
]]></verbatim>
<verbatim><![CDATA[
sub remove {
    my ($self, $sid, $options) = @_;
]]></verbatim>
<verbatim><![CDATA[
# 単純にidより連想されたデータを消去する必要があります
]]></verbatim>
<verbatim><![CDATA[
}
]]></verbatim>
<verbatim><![CDATA[
sub teardown {
]]></verbatim>
<verbatim><![CDATA[
my ($self, $sid, $options) = @_;
]]></verbatim>
<verbatim><![CDATA[
# このメソッドはセッションオブジェクトが破棄される直前に呼び出さ
# れます。
]]></verbatim>
<verbatim><![CDATA[
}
]]></verbatim>
<verbatim><![CDATA[
1;
]]></verbatim>
<verbatim><![CDATA[
__END__;
]]></verbatim>
<para>
上記の空白を埋めることで、あなたは
</para>
<verbatim><![CDATA[
$session = new CGI::Session("driver:MyDriver", $sid, {Option=>"Value"});
]]></verbatim>
<para>
とすることがき、後はCGI::Sessionのマニュアルにある通りです。
</para>
</sect2>
</sect1>
<sect1>
<title>著作権</title>
<para>
Copyright (C) 2002 Sherzod Ruzmetov. All rights reserved.
</para>
<para>
This library is free software. You can modify and distribute it under the same terms as Perl itself.
</para>
</sect1>
<sect1>
<title>作者</title>
<para>
Sherzod Ruzmetov &lt;sherzodr@cpan.org&gt;. 提案、コメント及びパッチは歓迎します。
</para>
</sect1>
<sect1>
<title>参考文献</title>
<list>
<item><para>
<link xref='CGI::Session'>CGI::Session</link> - CGI::Sessionマニュアル
</para>
</item>
<item><para>
<link xref='CGI::Session::CookBook'>CGI::Session::CookBook</link> - 実生活における実践的な解決方法
</para>
</item>
<item><para>
<strong>RFC 2965</strong> - &quot;HTTP State Management Mechanism&quot; は<xlink uri='ftp://ftp.isi.edu/in-notes/rfc2965.txt'>ftp://ftp.isi.edu/in-notes/rfc2965.txt</xlink> で見付けることが可能です。
</para>
</item>
<item><para>
<link xref='CGI'>CGI</link> - 標準のCGIライブラリ
</para>
</item>
<item><para>
<link xref='Apache::Session'>Apache::Session</link> - もう一つのCGI::Sessionに対する素晴らしき選択肢
</para>
</item>
</list>
</sect1>
<sect1>
<title>翻訳者</title>
<para>
三浦真磁&lt;snj@users.sourceforge.jp&gt;
</para>
</sect1>
</pod>
