register_globals = On の恐怖
PHP 4.2.0 (2003/05) から、register_globals の初期値が Off に変更されました。これによりこれまで稼動していたスクリプトや書籍に記載されているサンプルコードが動かないといった現象に戸惑った方もいらっしゃるかと思います。
セキュリティ上の理由で変更されたのですが、入門者や初心者にとっては PHP の都合に振り回されているように感じるばかりです。いくらかでもこの初期値の変更を理解していただきたいと思い、register_globals = On で何が危険なのか、どうすれば安全なコードを書くことが出来るか考えてみました。
上書きによる改ざん
register_globals = On の場合、環境変数や GET、POST などの PHP の外部からくる値は全て $変数名 という書式で使用することが出来ます。 では、同じ変数名が存在した場合はどうなるのでしょう。これは、variables_order によって優先順位が決まります。初期値は、"EGPCS"(Environment, GET, POST, Cookie、Server の頭文字)で、左から順番に変数に展開されます。よって GET と POST に同じ名前の変数があった場合には POST の値で上書きされることになります。
では、実際どんなことがおこる可能性があるのか考えてみます。
以下のコードはテンポラリーファイルのパスを示す環境変数 "TEMP" があり、"TEMP" の示すディレクトリ内のファイルを削除するスクリプトです。
<?php
//
// removeTemp.php
// テンポラリーファイルを削除する
//
// 例えば 環境変数 'TEMP' が
// '/home/username/temp/' であることを前提にしたコード。
$handle = opendir($TEMP);
if (is_resource($handle)) {
while (false !== ($file = readdir($handle))) {
if ('.' == $file || '..' == $file) {
continue;
}
if (is_file($TEMP.$file)) {
unlink($TEMP.$file);
}
}
closedir($handle);
}
?>
さてこのスクリプトに removeTemp.php?TEMP=%2F とリクエストをしたらどうなるでしょう。register_globals = On では環境変数(Environment)が GET で上書きされますので、$TEMP の値は '%2F' が urldecode された値 '/' となります。
さあ、'/home/username/temp/' を '/' に置き換えると何が起こるか考えてみてください。ルートディレクトリ内のファイルを全て削除することになります。
先ずこのスクリプトを誰でもアクセスできるところに置いてることが一番の問題ではあります。また実際にはファイルのアクセス権が無いとファイルが削除されることはありませんが、register_globals = On 環境下では、上書きによって値が改ざんされ、想定外の動作をする危険を含んでいるということです。
上書きされないスーパーグローバル変数
PHP4.1.0 から採用された「スーパーグローバル変数($_ENV, $_GET, $_POST, $_COOKIE, $_SERVER)」は、register_globals = On 環境下でも上書きされることはありません。想定外の動作を防ぐには「スーパーグローバル変数」を使うことが最良の対策です。
初期化されてないグローバル変数
register_globals = On の場合、初期化されていないグローバル変数を参照しているコードが実行される可能性があります。
例えば、パスワードが一致していればログオン時のコードを実行し、不一致であればエラーメッセージを表示する以下のようなコードがあったとします。
<?php
//
// isLogon.php
// パスワードを確かめてログオン時の処理を実行する
//
if($password == '1234') {
$Logon = true;
} else {
echo 'パスワードが違います。';
}
if($Logon) {
// ログオン時の処理
echo 'ログオンしました。';
}
?>
パスワードが一致しない限りログオン時の処理が実行されることはないように見えますが、 /isLogon.php?Logon=1 とリクエストすると、パスワードに関係なくログオン時の処理が実行されてしまいます。
スクリプトの中だけで使用するグローバル変数 $Logon が、外部から初期化されることを想定していないためにこのような問題が起こります。register_globals = On の場合、グローバル変数は、ユーザに対してもグローバルであることを認識しておきましょう。
グローバル変数は初期化しておく
グローバル変数をあらかじめ初期化しておけばこのようなことは起こりません。この例ならスクリプトの始めに $Logon = false; を記述しておけば、パスワードが一致しない限りログオン時のコードが実行されることは無かったのです。
register_globals = On で安全なコードを書くポイント
セキュリティーホールになる可能性が高い register_globals = On でも、次の事に留意していれば安全なコードを書くことが出来ます。
- 外部から来る値を参照する場合はスーパーグローバル変数を使用する。
- グローバル変数はあらかじめ初期化する。
PHP の言語仕様は柔軟であると云われます。言い換えれば「あいまい」な分、リスクも背負っていることになります。プログラマの技量が試される言語です。
PHP6 では、register_globals の設定そのものが廃止されるそうです。
