PHPのセッションアダプション脆弱性は修正して当然の脆弱性

(Last Updated On: 2018年8月18日)

PHPのセッションアダプション脆弱性がどのように影響するのか、広く誤解されているようです。セキュリティ専門家でも正しく理解していないので、一部のPHP開発者が正しく理解しなかったのは仕方ないのかも知れません。

Webセキュリティの第一人者の一人である徳丸氏は「PHPのセッションアダプション脆弱性は脅威ではない」と書いていますが、いろいろ間違いです。私は2005年頃から現在まで様々な機会に問題であると何度も繰り返し説明しており、しばらくすれば間違いに自分で気付くのでは?と思っていたので特に反論していませんでした。しかし、まだ気づかれていないようなので、幾つかある攻撃パターンのうち解りやすい例を1つだけ紹介しておきます。

以下の簡単なPHPスクリプトを利用します。興味がある方は実験してみてください。結果はブラウザに影響されます。私はLinux x86_64でGoogle製のChromeを使っています。

sess1.phpスクリプト

<?php
// Default is no domain and no path(/) for session cookie
$sessname = ‘app1’;
$domain   = ”;
$path     = ‘/’;
//$path     = ‘/php_session_explained’;
session_name($sessname);
session_set_cookie_params(0, $path, $domain);
session_start();
$old=session_id(); // Must have random ID
session_regenerate_id();
echo ‘<pre>’;
var_dump($old, session_id()); // Must have new ID
$_SESSION[‘last_val’] = $_SESSION[‘val’];
$_SESSION[‘val’] = time();
var_dump($_SESSION,$_COOKIE);
<?php
// Default is no domain and no path(/) for session cookie

$sessname = 'app1';
$domain   = '';
//$path     = '/';
$path     = '/php_session_explained';

session_name($sessname);
session_set_cookie_params(0, $path, $domain);

session_start();
$old=session_id(); // Must have random ID

session_regenerate_id();

echo '<pre>';
var_dump($old, session_id()); // Must have new ID
$_SESSION['last_val'] = $_SESSION['val'];
$_SESSION['val'] = time();

var_dump($_SESSION,$_COOKIE);

このスクリプトをPHPがインストールされたWebサーバのドキュメントルート以下に作ったディレクトリ

/php_session_explained

に配置します。まず、

http://127.0.0.1/php_session_explained/sess1.php

にアクセスします。初回は未初期化変数のエラーが発生しますが、通常通りランダムなセッションIDで初期化され、session_regenerate_id()で新しいランダムIDが生成されている事が分かります。以下は出力の例です。

string(26) "d4tqpaheo41c06evhvs6elrnq4"
string(26) "ut424a7nc160p26b1pqkj4q7c7"
array(2) {
  ["last_val"]=>
  int(1355045697)
  ["val"]=>
  int(1355045698)
}
array(1) {
  ["app1"]=>
  string(26) "d4tqpaheo41c06evhvs6elrnq4"
}

この動作は期待される通りの動作です。では、次にソースコードの$pathをスクリプトが配置されているパスに設定します。

//$path     = '/';
$path     = '/php_session_explained';
となっている部分を
$path     = '/';
//$path     = '/php_session_explained';
と変えます。もう一度

http://127.0.0.1/php_session_explained/sess1.php

にアクセスしてみましょう。

string(26) "kld1i8ll29bv2k7pehk47t6bh6"
string(26) "ph72u5jv8raof5tc7dhs371tb4"
array(2) {
  ["last_val"]=>
  int(1355036403)
  ["val"]=>
  int(1355036602)
}
array(1) {
  ["app1"]=>
  string(26) "kld1i8ll29bv2k7pehk47t6bh6"
}

今度は何度アクセスしても太字の部分が更新されません。session_regenerate_id()で更新したハズのセッションIDが使われていないからです。これは、パスでWebアプリを分割している場合、Javascriptインジェクション脆弱性によってどのアプリのセッションIDでも盗める可能性があることを意味します。この例はパスでしたが、アプリ分割にサブドメインを利用している場合でも同様の攻撃が可能です。全てのアプリケーションがアダプション脆弱性に対して強固であれば良いのですが、有効な対策があるにも関わらず利用せず、システムが強固であることを期待するのは正しいセキュリティ対策とは言えません。

徳丸氏はいわゆるccTLDなどに設定されたクッキーが有効になってしまう古いクッキーモンスター(ブラウザのバグ)と勘違いしているようですが(申し訳ないが、そこまで読んで徳丸さんの記事は100%間違っている事は明らかなので最後まで読んでいません)、いわゆるクッキーモンスター問題とは関係ありません。Web開発者はWebセキュリティ専門家にとっては一つのリクエストに対して複数のクッキーが設定できる事は常識です。(クッキーはパス、ドメイン、トランスポート、JS利用など複数の設定の組み合わせが可能で1つのリクエストでも複数のクッキーが送信候補になる)複数クッキーが設定された場合、ブラウザがどのクッキーを優先するのか?という当たり前の動作が、セッションアダプションに脆弱なセッション管理機構では問題になります。

普通に仕様を理解していれば直感的に解る事だと思うのですが、直感的ではないのかな?どうなのでしょう?

少し脱線しますが、複数クッキーが設定された場合の優先順位はブラウザによって異なります。これを今更「バグ」と言って統一する事は、今のクッキーを使っている限り無理です。今まで動いていたアプリがクッキー優先順位変更で突然動かなくなるかも知れない状況で、どちらかが歩み寄るとは思えないからです。仮に統一してもセッションアダプションに利用される問題などのセキュリティ問題の解決策にはならないので尚更です。Webセキュリティの要であるクッキーですが、仕様は出鱈目でした。しかし、少しずつ改善してはいます。現在のブラウザは昔よりマシになっていて、複数のクッキーが設定されていてもクッキーを1つしか送らなくなりました。この為、昔のようにサーバ側でどのクッキーを優先するかによって値がバラバラになる、という問題は解消されています。

PHPのセッション管理機構がセッションアダプションに脆弱でなければ、session_regenerate_id()を呼び出しの有無に関わらずセッションIDの安全性は維持されます。Webセキュリティの要であるセッションID管理がアダプションに脆弱であるのは問題です。ほとんどの開発者も同様に考えている証拠に、他のプラットフォームではセッションアダプション脆弱性が見つかると直ぐに修正されています。アダプションに脆弱なセッション管理機構とアプリが組み合わさると、ブラウザ側でセッションIDを固定させてセッションの盗む標的型攻撃も簡単であり、サーバ側で対応できる脆弱性なので当然です。セッションIDを更新する関数を追加するだけで終わらせてしまったのは、PHPだけではないでしょうか?

他のメジャーなWebアプリケーションプラットフォームのセッション管理機構では、セッションアダプション問題は脆弱性としてとっくに修正されています。しかし、2005年頃にはパッチが提案されていたにも関わらず未だに残っているのは、PHP開発者・Webセキュリティ維持に関わる者の一人として恥ずかしい事だと思っています。

欠陥があるのでPHPでは絶対にベストプライスに従ってセッションIDを更新しましょう、なら解るのですが「Webセキュリティの要であるセッション管理の欠点を指摘・修正しようとしていること」をまるで不必要であるかのにように批判する文章を書く意図が私には理解できません。セキュリティ専門家の一人としては、PHPプログラマのケアレスミス1つで、大昔から知られている既知の欠陥を使ってセッション固定化ができるセッション管理機構こそ批判されるべき、と考えています。

セッションアダプションとセッションIDコリージョン(ハッシュ衝突を検出し回避します)を修正するパッチはもう書いているのですが、PHPプロジェクトのGit移行へのドタバタでまだマージはされていません。このブログで何度か言及しましたが、7年前(2005年頃)からアダプション脆弱性修正パッチは存在していましたが適用されていませんでした。その間、何度か提案しましたが実現しませんでした。今回は脆弱性修正パッチが適用される事は決まっています!今ならほぼ全てのアプリはsession_regenerate_id()を使っているハズなので、少し延びるくらいは構わないですよね?PHPerの皆さん?

P.S. こんな余計なエントリを書く時間を使えば、古いソース用のパッチを最新gitソースツリーの4つのブランチにmergeしてPULLリクエストを送れてますね。。PHPerの皆さん、申し訳ない。

投稿者: yohgaki