register_globals 設定値による変数スコープの違い
php.ini 構成ディレクティブである register_globals は、外部から入力されたデータを PHP スクリプト内でどのように変数として利用可能にするかを制御します。この設定値は On または Off で指定され、変数の可視性に大きな影響を与えます。
以下のフォーム送信例を用いて、設定値による挙動の違いを確認します。
<form action="" method="get">
<input type="text" name="member_id" value="user01" >
<input type="submit" name="action_btn" value="send">
</form>
<?php
echo 'member_id::' . $member_id;
echo '<br>action_btn::' . $action_btn;
echo '<br>GET Array::';
print_r($_GET);
?>
register_globals = On の場合、実行結果は以下のようになります。
member_id::user01
action_btn::send
array ( [member_id] => user01 [action_btn] => send )
一方、register_globals = Off の場合、実行結果は以下のようになります。
member_id::
action_btn::
array ( [member_id] => user01 [action_btn] => send )
この結果から、設定が On の場合は HTTP リクエストから渡された値が自動的にグローバル変数として登録され、直接参照可能になります。逆に Off の場合は、$_GET や $_POST といったスーパーグローバル配列を通じて明示的に値を取得する必要があります。
セキュリティリスクと無効化の推奨理由
PHP 4.2.0 以降、このディレクティブのデフォルト値は Off に変更されました。サーバー環境を制御できない場合や、コードの移植性を考慮すると、最初から Off を前提とした開発を行うべきです。
この設定が On になっている場合、クライアントから送信された任意の变量がスクリプト内の変数として注入される可能性があります。PHP は変数の初期化を必須としていないため、予期せぬ変数上書きによるセキュリティホールが発生しやすくなります。
以下のコードは、認証ロジックにおける脆弱性の例です。
<?php
// 認証成功時にフラグを立てる
$is_permitted = false;
if (verify_credentials()) {
$is_permitted = true;
}
// 変数初期化がない場合、register_globals が On だと
// URL で is_permitted=true を渡すことで認証を bypass できる
// 例:auth.php?is_permitted=1
if ($is_permitted) {
include "/secure/internal_data.php";
}
?>
register_globals が有効な環境では、URL パラメータなどで $is_permitted を外部から操作され、認証を迂回される危険性があります。無効化されていれば、スーパーグローバル配列経由でしか値を取得できないため、このようなインジェクションは防止されます。また、変数を事前に初期化しておくことも重要な防御策となります。
無効化環境でのレガシーコード動作模拟
共有サーバーなど register_globals が無効化されている環境で、この機能を前提とした古いプログラムを動作させる必要がある場合、以下のスクリプトで挙動を模拟できます。variables_order の設定変更に応じて、対象とするスーパーグローバル配列を調整してください。
<?php
// register_globals On の模擬
if (!ini_get('register_globals')) {
$global_sources = array($_SERVER, $_ENV, $_FILES, $_COOKIE, $_POST, $_GET);
if (isset($_SESSION)) {
array_unshift($global_sources, $_SESSION);
}
foreach ($global_sources as $source) {
extract($source, EXTR_SKIP);
}
}
?>
有効化環境でのセキュリティ対策模拟
逆に、設定が有効になっているサーバーでセキュリティリスクを低減したい場合、スクリプトの冒頭で以下の処理を実行し、グローバル変数の注入を防ぐことができます。セッションを使用する場合は、session_start() の後に呼び出してください。
<?php
// register_globals Off の模擬
function disable_global_injection() {
if (!ini_get('register_globals')) {
return;
}
// GLOBALL 変数の上書き試行を検知
if (isset($_REQUEST['GLOBALS']) || isset($_FILES['GLOBALS'])) {
die('Detected attempt to overwrite GLOBALS');
}
// 削除対象から除外する予約変数名
$protected_keys = array('GLOBALS', '_GET', '_POST', '_COOKIE', '_REQUEST', '_SERVER', '_ENV', '_FILES');
$input_data = array_merge($_GET, $_POST, $_COOKIE, $_SERVER, $_ENV, $_FILES, isset($_SESSION) && is_array($_SESSION) ? $_SESSION : array());
foreach ($input_data as $key => $value) {
if (!in_array($key, $protected_keys) && isset($GLOBALS[$key])) {
unset($GLOBALS[$key]);
}
}
}
disable_global_injection();
?>