配置:注册全局变量

什么是注册全局变量?

PHP 中的一个常见安全问题是 register_globals 配置设置。该设置(可以为 On 或 Off)决定是否将 EGPCS(环境变量、GET、POST、Cookie、Server)变量的内容注册为全局变量。例如,如果 register_globals 设置为 On,那么 URL http://www.example.com/test.php?id=3 会自动将 $id 声明为全局变量,而无需任何代码。同样,$DOCUMENT_ROOT 也会被定义,因为它是 $_SERVER 超全局数组的一部分。这两个例子等同于在脚本开头放置以下代码:

$id = $_GET['id'];
$DOCUMENT_ROOT = $_SERVER['DOCUMENT_ROOT'];

这一特性是一个极大的安全隐患,您应该确保所有脚本的 register_globals 设置为 Off(从 PHP 4.2.0 开始默认设置为 Off,从 5.4.0 开始完全移除此设置)。更推荐使用 PHP 的预定义变量,例如超全局变量 $_REQUEST。更安全的做法是使用更具体的变量,例如:$_ENV$_GET$_POST$_COOKIE$_SERVER,而不是使用更通用的 $_REQUEST

示例

假设以下 PHP 代码位于接收表单数据的页面上,用户刚刚输入了错误的密码。$_POST 数组变量处理了这个情况。代码的作用是:如果密码正确(假设正确的密码是 "12345"),变量 $admin 会被设置为 TRUE。网站的配置规定,如果 $admin 被设置为 TRUE,该用户将获得管理员权限。以下代码兼容 PHP5。

if (isset($_POST['password']) && $_POST['password'] == "12345") {
    $admin = TRUE;
}

register_globals 不区分 $_POST 变量和 $_GET 变量。因此,假设用户提交了表单并访问了上面的页面。用户可能决定即使不知道密码,也应该获得管理员权限。因此,用户可以在页面 URL 后追加 ?admin=1。这时,如果 register_globals 设置为 On,将强制创建变量 $admin 并将其值自动设置为 1,相当于设置为 TRUE。这样,register_globals 为 On 时允许用户将变量和数据注入程序!在这种情况下,无论用户是否输入正确的密码,他们都可以仅通过操作 URL 获得管理员权限。

如您所见,任何懂得如何查看页面代码并分析的人,都可能在任何时候利用此类漏洞。这种情况可能发生在其他许多场合。

另一个例子是会话。当 register_globals 为 On 时,我们也可以使用 $username,如下面的示例所示,但要意识到,$username 可能来自其他地方,例如通过 GET(URL)。

// 我们无法知道 $username 来自哪里,但知道 $_SESSION 是会话数据
if (isset($_SESSION['username'])) {
    echo "Hello <b>{$_SESSION['username']}</b>";
} else {
    echo "Hello <b>Guest</b><br />";
    echo "Would you like to login?";
}

最佳实践

避免此问题的最佳方法是确保在 php.ini 中将 register_globals 设置为 Off。但作为一般的编码建议,始终初始化您的变量。以下代码对先前的代码示例做了一个小改动,首先将 $admin 设置为 FALSE,这样用户只能通过条件语句获得管理员权限:

$admin = FALSE;
if (isset($_POST["password"]) && $_POST["password"] == "12345") {
    $admin = TRUE;
}

filter_input()

由于超全局数组访问容易受到代码注入攻击[1],因此出于安全考虑,最好使用 filter_input() 语法[2]:

<?php
echo filter_input(INPUT_GET, 'password'); // 好
echo $_GET['password'];                   // 不好
?>

参考文献

更多信息

Last modified: Friday, 10 January 2025, 2:32 AM