extract() 默认覆盖变量,php 7.1 仍用 extr_overwrite,易致 $is_admin 等关键变量被恶意覆盖;应显式指定 extr_skip 或 extr_if_exists,最优解是白名单+filter_input() 显式赋值。

extract() 默认行为就是覆盖,不设 flags 就等于开后门
PHP 7.1 中 extract() 仍默认使用 EXTR_OVERWRITE,只要数组里有和已有变量同名的键,就会直接覆盖。比如代码里已有 $is_admin = false,而用户 POST 了 is_admin=1,extract($_POST) 之后这个变量就变成 true 了——验证逻辑瞬间失效。
必须显式指定 extract_type,优先用 EXTR_SKIP 或 EXTR_IF_EXISTS
别依赖默认值,也别用 EXTR_OVERWRITE。真实项目中应始终传入第二个参数:
-
EXTR_SKIP:遇到已存在变量直接跳过,最保守,适合大多数表单处理场景 -
EXTR_IF_EXISTS:只导入当前作用域中已存在的变量名(反向思维,慎用) - 绝对避免裸调用
extract($_POST)或extract($_GET)
示例:extract($_POST, EXTR_SKIP) —— 这样即使攻击者传 pass=xxx&thepassword_123=xxx,也不会覆盖服务端预定义的 $thepassword_123。
更安全的做法:白名单 + 显式赋值,绕过 extract
extract 的本质风险在于“自动映射”,而业务逻辑通常只需要几个固定字段。与其冒险过滤,不如放弃自动导入:
- 用白名单明确声明要接收的键:
$allowed = ['username', 'email', 'age']; - 手动提取:
$username = $_POST['username'] ?? '';或$data = array_intersect_key($_POST, array_flip($allowed)); - 配合
filter_input()做类型校验,比如filter_input(INPUT_POST, 'age', FILTER_VALIDATE_INT)
这样既杜绝覆盖,又天然防御类型混淆、空字节注入等衍生问题。
注意 extract 的第三个参数 prefix 不解决根本问题
加 prefix(如 extract($_POST, EXTR_OVERWRITE, 'req_'))只是把所有变量名加上前缀,生成 $req_username 这类变量。但它不阻止你后续再写 $username = $req_username; —— 覆盖风险只是延后,没消失。而且 prefix 容易漏写、难维护,实际项目中几乎没人靠它防漏洞。
真正容易被忽略的是:哪怕你用了 EXTR_SKIP,如果前面有 $$、parse_str() 或未校验的 $_REQUEST 合并操作,覆盖链依然可能成立。变量覆盖从来不是单点问题,而是上下文里的信任边界是否被反复击穿。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











