在PHP开发中,超全局变量如$_GET、$_POST、$_REQUEST、$_COOKIE等可以在任何作用域直接访问,它们承载着用户可控的外部输入。如果对这些输入不做任何处理就用于数据库查询、页面输出或命令执行,就会引入注入、跨站脚本等安全风险。因此,过滤超全局变量输入不是可选项,而是Web应用的基础防线。

为什么不能直接使用超全局变量
超全局变量的设计初衷是方便开发者获取环境数据,但这也意味着任何用户都可以通过构造请求来篡改其内容。例如一个接收用户ID的接口,若直接把$_GET['id']拼入SQL语句,攻击者可传入“1 OR 1=1”来绕过逻辑。很多安全漏洞的根源,就是开发者默认外部输入是“友善且规范”的。
另一个容易被忽视的问题是数据类型混乱。超全局变量中的所有值默认都是字符串,即便你期望收到数字。若未做类型约束,比较运算和函数参数就可能产生意料之外的行为。通过统一的过滤层,我们既能完成安全清洗,也能顺带完成类型转换,让后续业务代码更稳健。
使用filter扩展进行基础过滤
PHP内置的filter扩展提供了一组标准化函数,其中filter_input和filter_var最为常用。前者直接从超全局变量中按类型提取并过滤,后者对已有变量进行过滤。它们支持多种预定义过滤器,如整数、邮箱、URL、字符串清理等,比手写正则更安全且易读。
下面示例展示如何从$_GET中安全获取一个整数ID,并获取一个经过清洗的邮箱地址:
<?php
// 从GET中获取id,要求必须是非负整数
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT, array(
'options' => array('min_range' => 1)
));
if ($id === false || $id === null) {
die('非法的ID参数');
}
// 从POST中获取邮箱并清洗
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
if ($email === false) {
die('邮箱格式不正确');
}
echo 'ID: ' . $id . ', Email: ' . $email;
?>
上面的代码通过FILTER_VALIDATE_INT限制参数必须为整数且不小于1,若验证失败返回false;邮箱则使用FILTER_VALIDATE_EMAIL做格式校验。这种写法避免了手动判断带来的遗漏,也清晰表达了业务对数据的预期。
封装统一的输入获取层
在真实项目中,更推荐建立一个输入门面类,禁止业务代码直接触碰$_GET、$_POST。这样所有过滤规则集中维护,新人也不会误用原生数组。以下示例给出一个简单的Input助手:
<?php
class Input {
public static function get($key, $filter = FILTER_SANITIZE_STRING, $options = array()) {
$val = filter_input(INPUT_GET, $key, $filter, $options);
return $val;
}
public static function post($key, $filter = FILTER_SANITIZE_STRING, $options = array()) {
$val = filter_input(INPUT_POST, $key, $filter, $options);
return $val;
}
public static function int($key, $method = INPUT_GET, $min = null, $max = null) {
$opts = array();
if ($min !== null) $opts['min_range'] = $min;
if ($max !== null) $opts['max_range'] = $max;
return filter_input($method, $key, FILTER_VALIDATE_INT, array('options' => $opts));
}
}
// 使用方式
$page = Input::int('page', INPUT_GET, 1, 100);
$name = Input::post('name', FILTER_SANITIZE_STRING);
?>
该封装把常用的整数范围校验、字符串清洗都收敛到静态方法中。当项目规则变化,比如字符串过滤要额外去掉控制字符,只需修改Input类,而不必翻找散落在各处的$_POST读取点。
需要注意的是,FILTER_SANITIZE_STRING在PHP 8.1后已被废弃,新项目可改用htmlspecialchars配合类型转换,或采用FILTER_UNSAFE_RAW后再自行转义。封装层的优势正在于此:底层API调整时,业务代码零改动。
白名单与业务级校验
内置过滤器解决的是格式问题,业务合法性还需白名单约束。例如排序字段只允许“time”或“price”,即便它是字符串且无害,也不该接受其他值。此时应先过格式过滤,再过业务白名单。
<?php
$sort = Input::get('sort', FILTER_SANITIZE_STRING);
$allow = array('time', 'price', 'hot');
if (!in_array($sort, $allow, true)) {
$sort = 'time'; // 非法值回退到默认
}
?>
这种“先清洗、再裁决”的模式,能有效隔离外部输入与内部逻辑。结合前面的封装类,可把白名单作为参数传入,使校验逻辑复用度更高。过滤超全局变量输入的本质,就是建立一道从不可信到可信的转换屏障,让上层代码永远只看到干净、确定、符合预期的数据。