徳丸さんから「ブログ読みました。サンプルも動かしました。問題は分かるのですが、セッションアダプションがないPHPだと、何が改善されるのかが分かりません。教えて下さい 」とあったのでツイッターで返信するには少し長いのでこちらに書きます。まだ直していない脆弱性を詳しく解説するのはあまりよくないのですが、今なら影響を受けるアプリはほぼないと思うので構わないでしょう。
まず前提として、Webサーバと同様にJavascriptからもクッキーが設定できます。Javascriptインジェクションに脆弱なコードがサイトに1つでもあると、サイト上のセッションアダプション脆弱性をもつアプリへの攻撃が可能になります。この攻撃を緩和する対策もありますが、それはそれ、これはこれなので省略します。
セッションアダプションに脆弱なセッション管理機構で困る代表的なケースは以下の通りでしょう。
- ログイン時にsession_regenerate_idを呼んでない
- 自動ログインなど通常のセッション用以外でsession_regenerate_idを呼んでいない
- session_regenerate_idを呼んでいても、途中で保存している
- session_regenerate_idを呼んでいても、途中で実行パスが変えられる
1.は当たり前なので説明は省略しますが、アダプション脆弱性を利用できる状況だとログインセッションを乗っ取られます。
2.は、お薦めする方法ではありませんが、複数のセッションを用いて自動ログインを実装する方法です。php.ini設定でセッションデータの寿命は保存場所を変えられるので、自動ログイン用に寿命の長いセッションを別つくる事ができます。通常セッション用のみでなく、こちらもセッションIDを再生成しないとログインセッションを乗っ取られます。
3.は、2を実装する場合などに作ってしまいそうな脆弱性です。現在のログインセッションを作った後に、session_regenerate_idを呼ぶ前にsession_write_closeを使って書きこむと、既知のセッションIDを持つログインセッションが出来ます。
4.は、ログイン処理中に実行パスが変えられる場合に問題になります。ログイン処理中に前回のログイン状態を再生するなどの操作を行う場合、オブジェクトの再生成などでエラーや例外が発生し、プログラマが意図しない実行パスが変えられてしまい、session_regenerate_idが呼ばれない場合があります。これは多少気づきづらいとは思いますが、同類の攻撃が可能な脆弱性がPHP内部に存在して問題になった事があります。PHPに限らず例外などで実行パスが変えられてしまう事により問題となる事が時々あります。
session_regenerate_idのコードは単純なのでCプログラマ以外の方でも見れば何をしているのか解ると思います。
static PHP_FUNCTION(session_regenerate_id)
{
zend_bool del_ses = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &del_ses) == FAILURE) {
return;
}
if (SG(headers_sent) && PS(use_cookies)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot regenerate session id - headers already sent");
RETURN_FALSE;
}
if (PS(session_status) == php_session_active) {
if (PS(id)) {
if (del_ses && PS(mod)->s_destroy(&PS(mod_data), PS(id) TSRMLS_CC) == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Session object destruction failed");
RETURN_FALSE;
}
efree(PS(id));
PS(id) = NULL;
}
PS(id) = PS(mod)->s_create_sid(&PS(mod_data), NULL TSRMLS_CC);
PS(send_cookie) = 1;
php_session_reset_id(TSRMLS_C);
RETURN_TRUE;
}
RETURN_FALSE;
}
del_sesはPHP5.1.0から追加されたパラメータで、古いセッションデータを削除するオプションです。3のケースでもdel_sesがオプションでなければ、デフォルトで古いデータは消されるので問題にならないのですが、オプションなので消されない可能性が高くなります。
この実装を見て分かるようにsession_regenerate_idは基本的にはsession_id(md5(‘random string’))と変わりません。セッションアダプションに脆弱な場合でも、IDを変えて別のセッションデータにログイン状態を保存すれば、アダプションによってセッションIDが固定化されていても影響を受けずに済みます。PHPのWikiなどでは、実行パスの変更など条件は詳しく書いていませんが慣れているのか、どうして?という質問はもらいませんでした。
そもそも、セッションIDの再生成は盗聴などによるセッションID漏洩によるセッションハイジャックのリスクを緩和する為の対策です。それがセッションアダプションアダプション脆弱性の回避策として使えるので、問題がない使い方をしていればアダプション脆弱性を残しておいても良いことにはなりません。HTTPSを使っているからセッションIDをログイン・ログオフのイベントや定期的に変更しなくても良い、とは普通言いません。盗聴のリスクは少なくなりますが、万が一のセッションID漏洩の影響を緩和する為に適当に更新してください、と言うはずです。
先回りして議論しておきます。session_regenerate_idを呼ぶのは当然なので、アダプション脆弱性は脅威ではない、と主張することはセキュリティ専門家としておかしいです。この論法はセキュリティ対策の原則である多重のセキュリティ、フェイルセーフを否定しています。もし、この論法が正しいなら「文字エンコーディングのバリデーションはアプリの入力バリデーションを行う事が当然」としている私の場合、データベースやXML処理系などに文字エンコーディングベースの脆弱性があっても脅威ではなく、取るに足らない問題と言ってもよい事になります。
PHPの一利用者として、セッションアダプション脆弱性は対策さえしていればたいした問題でない、と言うことは構わないし当然の認識だと思います。しかし、セキュリティ専門家として、セッションアダプション脆弱性は対策さえしていればたいした問題でない、修正も必要ない、とするような論調で書くのはおかしいと言わざるを得ません。私はPHPの利用者でもありますが、開発者でもあります。ですから簡単に修正できる脆弱性、しかもWebセキュリティの核心と言えるセッション管理の脆弱性を放置したまま「たいした問題ではない」(脅威ではない)と言うととても無責任だと思うので言えません。
PHPの利用者が、たまたまスキルの足りないPHPプログラマに当たりアダプション脆弱性でセッション盗み放題のサイト作られてしまい問題になったとします。PHP開発者がセッション管理には他のプラットフォームでは当たり前に修正されている問題はあると認識した上で、直しもしないで「脅威ではない」「間違った使い方をした人が悪い」と言っていたらやり切れない思いを持つのではないでしょうか?
脱線しましたが、何が改善されるのか?結論は
- ログイン時にsession_regenerate_idを呼んでない
- 自動ログインなど通常のセッション用以外でsession_regenerate_idを呼んでいない
- session_regenerate_idを呼んでいても、途中で保存している
- session_regenerate_idを呼んでいても、途中で実行パスが変えられる
という事を考えなくてもアダプションによるログインセッション乗っ取りが行えなくなる事が改善点です。
関連:
- 結局セッションID管理はどうすれば良いのか?
- http://blog.ohgaki.net/php-session-adoption-how-it-works-to-attack-application