php文件包含漏洞防御关键在于阻断“用户可控输入→包含函数执行”链路:1. php.ini中必须设allow_url_include=off;2. 代码层禁用动态拼接,强制使用白名单硬编码;3. 路径处理须用basename()或realpath()校验;4. 防御伪协议与日志包含需禁用危险函数并限制日志可读性。

PHP文件包含漏洞不是“修一次就完事”的问题,关键在于阻断“用户可控输入 → 包含函数执行”这条链路。只要 include、require、include_once、require_once 的参数能被外部(如 $_GET、$_POST)直接影响,且未做路径约束或白名单校验,漏洞就存在。
php.ini 中必须关闭 allow_url_include
这是远程文件包含(RFI)的开关,也是最基础、最常被忽略的防线。默认值虽为 Off,但老旧系统、某些CMS安装包或运维手动开启后极易遗漏。
- 检查当前值:
php -i | grep allow_url_include或在脚本中调用ini_get('allow_url_include') - 若返回
1或On,立即修改php.ini:设为allow_url_include = Off,然后重启 Web 服务(systemctl restart apache2或systemctl restart php-fpm) - 注意:不能只用
ini_set('allow_url_include', '0')—— 该函数在运行时无效,仅对部分配置项起作用 - 顺手检查
allow_url_fopen,虽然不直接导致 RFI,但会扩大攻击面(如配合file_get_contents读取远程内容)
代码层必须杜绝“动态拼接 + 无校验”模式
即使 allow_url_include 关闭,本地文件包含(LFI)仍可利用 ../ 跳转读取 /etc/passwd、日志、session 文件等,甚至结合伪协议(如 php://filter)读源码或写 WebShell。
- 禁止写法:
include $_GET['page'] . '.php';、require $config_dir . '/' . $_POST['cfg']; - 推荐做法:用白名单数组硬编码允许的文件名,不拼接、不解析扩展名、不信任任何用户输入的路径片段
- 示例修复:
$allowed = ['dashboard', 'profile', 'settings']; $page = $_GET['page'] ?? 'dashboard'; if (!in_array($page, $allowed)) { http_response_code(400); exit('Invalid page'); } include __DIR__ . '/pages/' . $page . '.php'; - 若必须支持更多页面,可用映射表替代字符串拼接:
$map = ['user' => 'user_controller.php', 'admin' => 'admin_panel.php'],再查键是否存在
路径处理时警惕 ../、空字节、编码绕过
只过滤 ../ 不够,攻击者会用 ..%2f、%00、....// 或双写等方式绕过简单 str_replace。
- 不要依赖
str_replace(['../', '..\'], '', $input)—— 易被绕过,且可能破坏合法文件名 - 正确方式:用
basename()提取文件名(丢弃所有路径信息),再与白名单比对;或用realpath()解析后判断是否落在预期目录内 - 示例安全提取:
$raw = $_GET['file'] ?? ''; $filename = basename($raw); // 自动剥离所有路径 if (!in_array($filename, ['a.php', 'b.php'])) { die('Forbidden'); } include __DIR__ . '/safe_dir/' . $filename; - 特别注意:Windows 下大小写不敏感、
与/混用、NTFS 替换流(file.php::$DATA)都可能成为绕过点,生产环境应统一用 Linux 部署并禁用 Windows 特性
伪协议和日志包含这类“无文件”利用怎么防
攻击者不传恶意文件,而是用 php://input、data://、php://filter 等 PHP 内置协议触发代码执行或源码泄露,这类攻击不依赖 allow_url_include,也不走文件系统,纯靠函数调用逻辑缺陷。
- 根本原则:所有包含操作的目标必须是明确存在的、预定义的、不可控的磁盘路径文件
- 禁用危险协议(非万不得已):在
php.ini中设置disable_functions = php_strip_whitespace,show_source,highlight_file,phpinfo,readfile,file_get_contents(注意别误杀业务函数) - 日志包含防御:确保 Web 服务器错误日志、访问日志不被 Web 可读(如 Nginx 日志权限设为
640,属主为root:www-data);避免在日志中记录User-Agent、Referer等可控字段,或提前过滤其中的 PHP 标签 - 最关键的其实是:永远不要把
include()的参数来源设为任意用户输入 —— 这条线一旦松动,后续所有加固都像给漏水的桶补胶带
真正难的不是写几行白名单代码,而是排查所有历史代码里那些藏在 eval()、create_function()、模板引擎 include、插件钩子回调里的间接包含点。一个没审计到的 require $plugin_path . '/' . $hook_name . '.php' 就足以让整套防护失效。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











