本文目录导读:

在 PHP 中,超全局变量(如 $_GET、$_POST、$_COOKIE、$_REQUEST、$_SERVER 等)的污染问题通常是指不可信数据被直接用于危险操作(如 SQL 注入、XSS 攻击、命令执行等)。
要“解”这个问题,核心原则是:永远不要信任外部输入,以下是系统的解决方案:
输入过滤与验证(白名单验证)
这是最根本的解法,不要先接收再“清理”,而是先验证“是否符合预期”。
推荐做法:
- 类型强转:
$id = (int) $_GET['id'];,如果不是数字,会变成0或1,杜绝注入。 - 正则校验(白名单):
// 只允许字母数字和下划线 if (preg_match('/^[a-zA-Z0-9_]+$/', $_GET['username'])) { $username = $_GET['username']; } else { // 拒绝访问或使用默认值 $username = 'guest'; } - 过滤扩展:使用
filter_var()或filter_input()。$email = filter_input(INPUT_GET, 'email', FILTER_VALIDATE_EMAIL); $ip = filter_input(INPUT_SERVER, 'REMOTE_ADDR', FILTER_VALIDATE_IP);
转义输出(针对 XSS 攻击)
无论数据怎么过滤,输出到 HTML 时必须转义。
// 标准 HTML 实体转义 $safe_html = htmlspecialchars($_GET['message'], ENT_QUOTES, 'UTF-8'); echo $safe_html;
注意: 如果数据会输出到 JS、URL、CSS 等不同上下文,需要不同的编码函数(如 json_encode、urlencode、rawurlencode)。
参数化查询(针对 SQL 注入)
永远不要拼接 SQL 字符串,使用预处理语句。
使用 PDO(推荐):
$pdo = new PDO('mysql:host=localhost;dbname=test', $user, $pass);
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id');
$stmt->bindParam(':id', $_GET['id'], PDO::PARAM_INT);
$stmt->execute();
或 MySQLi:
$stmt = $mysqli->prepare('SELECT * FROM users WHERE id = ?');
$stmt->bind_param('i', $_GET['id']);
$stmt->execute();
关键: 此时输入的 $_GET 字符串被当作字面量传输,数据库端拼装,绝对安全。
配置层面防护
修改 php.ini 或使用 ini_set() 来降低风险。
- 关闭
register_globals(PHP < 5.4 老版本漏洞):将变量注册为全局变量。register_globals = Off
- 设置
variables_order:控制超全局变量的注册顺序。variables_order = "GPCS"
- 谨慎使用
$_REQUEST:它合并了GET、POST和COOKIE,极易导致逻辑混乱。建议禁用或仅用于调试。request_order = "GP"
框架层面的抽象(最佳实践)
使用现代 PHP 框架(Laravel、Symfony、ThinkPHP 等)内置的请求对象。
// Laravel 示例
public function store(Request $request) {
$validated = $request->validate([
'title' => 'required|max:255',
'body' => 'required',
]);
// 业务逻辑...
}
框架会自动处理过滤、验证和上下文转义,开发者无需直接触碰丑陋的 $_GET。
临时解决已污染的变量(魔术方法)
如果项目已经存在大量裸露的超全局变量,且不方便立刻重构,可以使用一个安全封装函数过渡:
function get_safe($key, $default = '') {
if (isset($_GET[$key])) {
// 先转义,再返回,但注意后续拼接仍需注意上下文
return htmlspecialchars(trim($_GET[$key]), ENT_QUOTES, 'UTF-8');
}
return $default;
}
总结优先级
- 最安全:白名单验证 + 参数化查询 + 输出转义(三管齐下)。
- 中等安全:只用
filter_input+ 类型转换。 - 最脆弱:直接使用
$_GET/$_REQUEST进行 SQL 拼接或echo。
核心认知: 数据本身没有“脏”或“干净”,关键是在哪个环节、用哪种方式处理,所有超全局变量默认都应视为“恶意字符串”,只有在进入特定“执行上下文”(如 SQL、HTML、Shell)时,才针对性转化。