當 register_globals = On 時,PHP 會將 $_GET、$_POST、$_COOKIE 等超全局數組中的鍵值自動註冊為全局變量。攻擊者可通過 URL 參數覆蓋任意全局變量 (包括 $GLOBALS、配置變量、對象屬性)。
<?php
// config.php
$GLOBALS['db_host'] = 'localhost';
$GLOBALS['db_user'] = 'root';
$GLOBALS['db_pass'] = 'secret';
// index.php
require 'config.php';
// 正常邏輯使用 $GLOBALS['db_host'] 連接數據庫
攻擊請求:https://example.com/index.php?GLOBALS[db_host]=attacker.com&GLOBALS[db_user]=hacker
結果:$GLOBALS['db_host'] 被覆蓋為 attacker.com,導致連接被劫持、配置竄改、甚至繞過權限檢查。
function unregister_globals() {
// 1. 未開啟則無需處理
if (!ini_get('register_globals')) return;
// 2. 檢測 GLOBALS 覆蓋嘗試
if (isset($_REQUEST['GLOBALS']) || isset($_FILES['GLOBALS'])) {
die('GLOBALS overwrite attempt detected');
}
// 3. 不應被銷毀的超全局變量
$noUnset = ['GLOBALS', '_GET', '_POST', '_COOKIE', '_REQUEST', '_SERVER', '_ENV', '_FILES'];
// 4. 合併所有輸入源
$input = array_merge(
$_GET, $_POST, $_COOKIE, $_SERVER, $_ENV, $_FILES,
isset($_SESSION) && is_array($_SESSION) ? $_SESSION : []
);
// 5. 遍歷銷毀被註冊的全局變量
foreach ($input as $key => $val) {
if (!in_array($key, $noUnset) && isset($GLOBALS[$key])) {
$GLOBALS[$key] = null;
unset($GLOBALS[$key]);
}
}
}
// 在項目入口文件 (index.php) 最開頭調用
unregister_globals();
$_GET、$_POST、$_SESSION 等超全局數組。return ['host' => '...'];,而非設置 $GLOBALS。error_reporting(E_ALL); ini_set('display_errors', 0); 生產環境記錄日誌不顯示。filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT) 替代直接變量。<?php
// check_globals.php - 放在項目根目錄訪問一次
if (ini_get('register_globals')) {
echo "<h1 style='color:red'>⚠️ 危險:register_globals = On</h1>";
echo "<p>請立即在 php.ini 中設置 register_globals = Off 並重啟 PHP。</p>";
// 嘗試演示漏洞
if (isset($_GET['test'])) {
$GLOBALS['test_var'] = 'original';
echo "<p>注入前: {$GLOBALS['test_var']}</p>";
// 訪問 ?test=1&test_var=hacked
echo "<p>注入後: {$GLOBALS['test_var']}</p>";
}
} else {
echo "<h1 style='color:green'>✅ 安全:register_globals = Off</h1>";
}