トップ 差分 一覧 Farm ソース 検索 ヘルプ RSS ログイン

BBS-サポート掲示板/841

ページの作成や編集にはユーザ登録が必要です。

 Login 不具合事例:mod_rewrite 等による URL 書き換え併用時の cookie path 異常 - ぐうます (2008年09月10日 18時27分29秒)

概要

FSWiki において、mod_rewrite に代表される URL 書き換えを併用して運用する場合、生成されるクッキーの cookie path が異常となるため、Login 不具合(Session 継続不具合)が発生することがあるようです。

不具合事例

筆者は自サイトにて、mod_rewrite による URL 書き換えを併用して FSWiki を 2 年ほど運用してきましたが、週に 1 回程度の発生率で「Login に成功したにもかかわらず、ユーザ以上の権限でのみ閲覧可能なページについて『参照権限がありません』というエラーにより閲覧ができない」という問題が起きていました。たまにしか起きない問題であるため、原因がなかなか分からなかったのですが、調査の結果、どうやら mod_rewrite 併用時にはクッキーに付加する cookie path の値に問題があることが分かりました。そして、cookie path 生成部に修正を加えることにより、不具合が起きなくなることを確認しました(確認期間:1 ヶ月)。

Session の継続とクッキー

FSWiki の Session 管理は、概ね次のようにして行われます。

FSWiki の Session 管理概要

  1. Login が行われると、それにより開始された Session の情報(ログイン中のユーザ名など)は、FSWiki が動作する Web サーバ上に Session 情報ファイル (./log/cgisess_xxxxxx) として保存されます。
  2. FSWiki は、クライアントの Web ブラウザに対して、"CGISESSID" という名前のクッキーを送信し、Web ブラウザはこれを保存します。
  3. Web ブラウザは、あるページを閲覧しようとするとき、そのページの URL と前方一致するような cookie path をもつクッキーの値を Web サーバに送信します。このため、ログイン中のクライアントが FSWiki のページを閲覧しようとするとき、"CGISESSID" クッキーの値が Web サーバに送られます。
  4. FSWiki は、受け取った "CGISESSID" クッキーの値を基にして、継続中の Session に対応する Session 情報ファイル (./log/cgisess_xxxxxx; xxxxxx は "CGISESSID" クッキーの値) を特定することができ、ファイルの内容からどのような権限による閲覧なのかを知ることが出来ます。
  5. FSWiki は、得られた閲覧権限に基づいてリクエストされたページの html を生成してクライアントに返送するとともに、"CGISESSID" クッキーをクライアントに対して再度送信することにより、Session の有効期限を更新します。

以上のことから、"CGISESSID" クッキーに付加される cookie path とは、「その Session が有効となるページ URL 範囲」を意味することになります。FSWiki の Login は farm 単位で行われますので、ある 1 つの Session は、少なくともそれを開始した farm 内の全ページで有効となるべきです。これを実現するための最もシンプルな方法は、「Session を開始した farm 内の全てのページ URL に前方一致するような unique な値となるように cookie path の値を生成すること」です。

(注:本ページ内では、便宜上、root wiki の下にぶら下がる狭義の farm に加え、root wiki 自身も含めて "farm" と記述することにします。)

サブルーチン Util::cookie_path() の動作:mod_rewrite を使用しない、通常の FSWiki の場合

FSWiki においてクッキーを作成する際、cookie path は サブルーチン Util::cookie_path() を用いて生成されます。そのソースを下記に示します。

#===============================================================================
# <p>
#   Cookieのpathに指定する文字列を取得します。
# </p>
# <pre>
# $path = Util::cookie_path($wiki);
# </pre>
#===============================================================================
sub cookie_path {
  my $wiki = shift;
  my $script_name = quotemeta($wiki->config('script_name'));
  my $path = $ENV{'REQUEST_URI'};
  $path =~ s/\?.*//;
  $path =~ s/$script_name$//;
  return $path;
}

このサブルーチンの動作の概要は、次の通りです。

  1. 環境変数 "REQUEST_URI" の値(=「リクエストされた URL」からサーバ名を除いた文字列)を取得し、
  2. その値からクエリ文字列("http://example.jp/wiki.cgi?page=Page1" の "?" 以降の部分)を除去し、
  3. その値の末尾に $script_name に等しい部分があればそれを除去した値を cookie path として返却する。

具体例をいくつか示しますと、次のようになります。

Web ブラウザから要求された URL が "http://example.jp/wiki.cgi?page=Page1" の場合
項目
REQUEST_URI /wiki.cgi?page=Page1
script_name wiki.cgi
cookie path /

Web ブラウザから要求された URL が "http://example.jp/wiki.cgi/farm1?page=Page1" の場合
項目
REQUEST_URI /wiki.cgi/farm1?page=Page1
script_name farm1
cookie path /wiki.cgi/

Web ブラウザから要求された URL が "http://example.jp/wiki.cgi/farm1/farm2?page=Page1" の場合
項目
REQUEST_URI /wiki.cgi/farm1/farm2?page=Page1
script_name farm2
cookie path /wiki.cgi/farm1/

以上の例から明らかなように、mod_rewrite を使用しない通常の FSWiki として運用する場合の URL であるならば、生成される cookie path は、親 farm 内のページの URL には前方一致せず、その farm 内の全ページの URL には前方一致する、その farm 内で unique な値 になるため、ログインした同一 farm 内で『参照権限がない』エラーが発生することはありません。

サブルーチン Util::cookie_path() の動作:mod_rewrite を使用する場合

次に、mod_rewrite を使用する場合について考えてみます。例えば、BugTrack-plugin/350 での mod_rewrite 設定

RewriteRule ^/$ /wiki.cgi [L,NE,P,QSA]
RewriteRule ^/(.+)\.html$ /wiki.cgi?page=$1 [L,NE,P,QSA]

を用いたとすると、サブルーチン Util::cookie_path() の動作例は次のようになります。

Web ブラウザから要求された URL が "http://example.jp/Page1.html" の場合
項目
REQUEST_URI /Page1.html
script_name wiki.cgi
cookie path /Page1.html

この例から分かるように、生成される cookie path がそのページ固有の値となるので farm 内で unique な値になりません。このため、cookie path が異なる複数の "CGISESSID" クッキーが Web ブラウザに対して生成されるため、Session の継続に関する Web ブラウザと FSWiki との間の情報のやりとりがおかしくなってしまうようです。

対策の検討

mod_rewrite を使用しない場合と同様に、mod_rewrite を使用した場合でも、「1 つの farm 内では必ず unique な値となるように cookie path の値を生成すること」ができれば、本件は解決できそうです。上記した mod_rewrite 使用時の cookie path の例で何が問題かを考えると、「ページ名の部分が cookie path から除去できていないために、cookie path が各ページ毎に異なること」です。よって、cookie path からページ名の部分を除去するコードを追加することとし、次のような対策案としました。

対策案:(行頭 '+' が追加行, 行頭 '-' が削除行)

 sub cookie_path {
   my $wiki = shift;
   my $script_name = quotemeta($wiki->config('script_name'));
   my $path = $ENV{'REQUEST_URI'};
   $path =~ s/\?.*//;
-  $path =~ s/$script_name$//;
+  $path =~ s/[^\/]+$//;     # 末尾に '/' 以外の文字があれば除去する。
   return $path;
 }

なお、オリジナルのコードにある、末尾の $script_name を除去するコード

$path =~ s/$script_name$//;

は、対策案で追加した

$path =~ s/[^\/]+$//;     # 末尾に '/' 以外の文字があれば除去する。

で代用することができるので削除しました。以上より、オリジナルの FSWiki の動作に影響を与えることなく対策できることになります。

この対策を施した状態での mod_rewrite 使用時のサブルーチン Util::cookie_path() の動作例は次のようになります。

Web ブラウザから要求された URL が "http://example.jp/Page1.html" の場合 (対策後)
項目
REQUEST_URI /Page1.html
script_name wiki.cgi
cookie path /

よってこの対策により、mod_rewrite を使用しないときと同様に cookie path が正しく生成されるようになります。

制限事項

mod_rewrite の設定が複雑である次の例のような場合、上記対策案では問題が解決できません。

対策できない例:次のような 2 つの URL が、mod_rewrite により同一 farm 内のページ URL に書き換えられるような運用の場合、cookie path が farm 内で unique にならないため問題が解決できません。

  • http://example.jp/Page1.html → cookie path = /
  • http://example.jp/dir1/Page2.html → cookie path = /dir1/

このような運用では、各 farm 毎に手動で設定した値を cookie path として使用するなどの対策が必要になります。そのためには、管理者メニューから各 farm 毎に cookie path を手動で設定するインターフェースを準備する必要があるため、本ページでは検討外と致します。

patch

対策案の patch

--- ../wiki3.6.3dev3orig/lib/Util.pm	Mon Dec 31 12:05:37 2007
+++ lib/Util.pm	Wed Sep 10 17:27:53 2008
@@ -64,11 +64,9 @@
 # </pre>
 #===============================================================================
 sub cookie_path {
-	my $wiki = shift;
-	my $script_name = quotemeta($wiki->config('script_name'));
 	my $path = $ENV{'REQUEST_URI'};
 	$path =~ s/\?.*//;
-	$path =~ s/$script_name$//;
+	$path =~ s/[^\/]+$//;     # 末尾に '/' 以外の文字があれば除去する。
 	return $path;
 }
 

Util.pm.20080910.patch(528) として添付致します。本 patch は、FSWiki 3.6.3dev3 の lib/Util.pm に対して作成したものですが、変更箇所である lib/Util.pm の cookie_path() が FSWiki 3.5.7 以降変更がないので、内容的には FSWiki 3.5.7 以降に対しては適用可能と思われます。なお、本 patch では、対策によって使用しなくなった $script_name, 及び $wiki の処理も削除しています。これに伴い、本来であれば cookie_path() の呼び出し側でも引数 $wiki を与えなくてよくなるのですが、変更しなくても動作に支障がないこと、及び、上記制限事項で述べた cookie path の手動設定機能をもし実装することになったら再度復活することになると思いますので、呼び出し側についての変更は本 patch では実施していません。

まとめ

mod_rewrite 使用時に cookie path が異常になること、そしてその対策案を示しました。この対策案により、自サイトでは「Login しているのに『参照権限がない』エラーが発生する」不具合が解消しました。

お願い

もし、本件と同様に mod_rewrite 等を併用した運用にて Session 継続不具合が発生している方がいらっしゃいましたら、本対策案を試していただき、成否をご報告いただきたく、よろしくお願い致します。(理由:上記の検討内容が、筆者自サイトのあるレンタルサーバ固有の設定等に依存した内容になっておらず、広く一般に適用できる対策であることを確認するため。もしこれが一般的な対策であることが確認できたならば、BugTrack-plugin/350 へのマージ等を視野に入れることができます。)

お名前: コメント:
Util.pm.20080910.patch

最終更新時間:2010年10月19日 03時15分47秒