[PHP-users 35324] セッションハイジャックと session_regenerate_id() について

shinichiro mori shinichiro.mori.1983 @ gmail.com
2010年 8月 8日 (日) 13:21:04 JST


森といいます。よろしくお願いします。

■はじめに
セッションハイジャック対策として使用される事が多いと思われる session_regenerate_id() に
関して、疑問点があるので投稿します。

ここでは、攻撃者が勝手に用意したセッションIDでセッション変数が初期化されるセッション固定攻撃
と呼ばれるものに関しては考慮していません。
また、セッションはクッキーによってのみ受け付けるものとします。

被害に会う状況としては、以下のようなものを想定しています。
(1)攻撃者にブラウザのクッキーの内容を直接見られた。
(2)攻撃者がネットワークをモニターしてクッキーの情報を入手した。
など。

本文の内容に勘違いや間違った使用法などありましたら、併せてご指摘お願いします。

■開発環境
OS:		Ubuntu 10.04
SERVER:	Apache/2.2.14 (Ubuntu)
PHP:	PHP 5.3.2-1ubuntu4.2 with Suhosin-Patch (cli) (built: May 13 2010 20:01:00)

■サンプルファイル
先に検証に使用したファイルを載せておきます。


[DocumentRoot]/test/login.php
※ ログイン処理は省略します。
※ 便宜上、JavaScript でセッションクッキーを表示させておきます。
===============================================================================
<?php
ini_set("session.use_only_cookies", 1);
ini_set("session.save_path",        [セッション保存パス]);
ini_set("session.gc_probability",   100);
ini_set("session.gc_divisor",       100);
ini_set("session.gc_maxlifetime",   300);
session_name("SESSION");
session_start();
session_regenerate_id();
$_SESSION["userid"] = "mori";
?>
<html>
<head><title>ログインページ</title></head>
<body>
<?
echo "ようこそ、{$_SESSION["userid"]}さん";
?>
<hr />
<script type="text/javascript">
document.write(document.cookie);
</script>
</body>
</html>
===============================================================================


[DocumentRoot]/test/menu.php
※ $_SESSION から会員IDを取得して表示します。
※ 便宜上、JavaScript でセッションクッキーを表示させておきます。
===============================================================================
<?php
ini_set("session.use_only_cookies", 1);
ini_set("session.save_path",        [セッション保存パス]);
ini_set("session.gc_probability",   100);
ini_set("session.gc_divisor",       100);
ini_set("session.gc_maxlifetime",   300);
session_name("SESSION");
session_start();
session_regenerate_id(true);
?>
<html>
<head><title>会員メニュー</title></head>
<body>
<?php
if (isset($_SESSION["userid"])) {
	echo "あなたは、{$_SESSION["userid"]}さんですね?";
} else {
	echo "あなたは誰ですか?";
}
?>
<script type="text/javascript">
document.write(document.cookie);
</script>
</body>
</html>
===============================================================================


[DocumentRoot]/test/evil.php
※ 攻撃者が用意するページです。
※ PEAR HTTP_Request を使用してクッキーを付加し、menu.php に送信します。
※ レスポンスより、会員専用メニューの内容、送信されてきたクッキーを取得します。
===============================================================================
<html>
<head><title>攻撃者のスクリプト実行ページ</title></head>
<body>
<form method="POST" action="">
セッション値: <input name="session" value="" /><input type="submit" value="OK" />
</form>
<hr />
<?php
require_once("HTTP/Request.php");
$req = new HTTP_Request("http://localhost/test/menu.php");
if (isset($_POST["session"]) && !empty($_POST["session"])) {
	$req->addCookie("SESSION", $_POST["session"]);
} else {
	return;
}
$req->sendRequest();
?>
<h1>body</h1>
<?php
echo $req->getResponseBody();
?>
<hr />
<h1>クッキー</h1>
<pre>
<?php
echo var_dump($req->getResponseCookies());
?>
</pre>
</body>
</html>
===============================================================================

■検証手順
(1)
会員が login.php よりログインし、会員ID(ここでは mori )がセッションにセットされます。
この時セッションクッキーが "AAAAA" にセットされたとします。

(2)
会員が続けて menu.php にアクセスします。
この時セッションクッキーが "BBBBB" に再セットされたとします。

(3)
この時なんらかの経路で攻撃者が会員のセッションクッキーを入手し("BBBBB")、攻撃者のブラウザから
evil.php にアクセスして、セッションクッキー "BBBBB" をセットして menu.php へリクエストを
送信します。
この時、「あなたは、moriさんですね?」というレスポンスが得られ、更に再セットされた
セッションクッキー("CCCCC" とする)を得ます。

(4)
この時点で "BBBBB" は破棄されているので、会員がもう一度 menu.php にアクセスすると、
「あなたは誰ですか?」が表示され、もう一度ログインが必要な状態になります。
会員は何が起こったかよく分からず、いつも通りログイン処理からやり直すだけかもしれません。

(5)
一方で、攻撃者は有効なセッションクッキー "CCCCC" を入手済みなので、これを使用して再び menu.php
にリクエストを送信すれば、「あなたは、moriさんですね?」が表示され、新たなセッションクッキーを
入手し、以後繰り返し会員専用メニューを表示させることができます。

■質問内容
上のような状況が起こるのは、(2)で会員がしばらくの間ページ遷移等をしなかったためセッションIDが
再セットされないままとなり、その間に攻撃者の被害に会ってしまった、というようなケースかと思われますが、
session_regenerate_id() は menu.php では使用すべきではないということでしょうか?
もしくは、セッション変数をチェックして、内容が不正と判断される時のみ session_regenerate_id() を
使用する等、至るところで不用意に使用しない方が良いということでしょうか?

session_regenerate_id() を使用しない限り、セッションIDが盗まれても、
会員がログアウトするなり、session.gc_maxlifetime だけ時間が経過するなりして、
セッションが破棄された時点で攻撃者も会員専用メニューにアクセスできなくなると思います。


以上、長文になってしまいましたが、上記内容について、既知の問題なのか、間違った使い方なのか等含め、
コメント、ご意見など、よろしくお願いします。


PHP-users メーリングリストの案内