PHP路径遍历漏洞的常见绕过手法与防御配置

PHP路径遍历漏洞的常见绕过手法与防御配置

1024KB1024KB 网络安全与合规 2026-08-19 13:10:41 浏览 37419 字数 1594

PHP路径遍历漏洞是Web应用中最常见的安全威胁之一,其核心危害在于攻击者能够通过构造如../等特殊路径字符,突破文件访问限制,读取或执行服务器上的任意文件。据OWASP《Top 10 Web Application Security Risks》报告统计,路径遍历漏洞在真实Web应用中的检出率约8%,且常与文件包含、文件读取等功能结合,造成数据泄露或远程代码执行。防御思路应从输入过滤、运行环境限制和代码审计三个层面同时展开,单一措施无法完全阻断攻击。

路径遍历漏洞的原理与常见场景

路径遍历漏洞的根本原因是开发者未对用户输入的文件名或路径参数进行严格过滤,直接将其拼接至文件操作函数中。攻击者通过注入../(上级目录)或..\(Windows下)等序列,突破应用根目录限制,访问系统敏感文件。在PHP中,典型易受攻击的函数包括include()、require()、file_get_contents()、fopen()、readfile()等。常见场景如:

  • 文件下载功能:download.php?file=user_manual.pdf,攻击者修改为file=../../../etc/passwd。
  • 模板加载:index.php?template=home,攻击者利用template=../../../../etc/passwd%00(空字节截断,PHP < 5.3.4时有效)。
  • 图片显示:showImage.php?img=avatar.jpg,修改为img=../../config.php。

常见的绕过手法

攻击者并非简单地使用../,而是通过多种编码和变形技术绕过应用层的过滤。以下为五种典型绕过手法,并附其原理与示例。

手法原理示例
URL编码将../中的点或斜杠进行URL编码,如%2e%2e%2f(.. /)file=%2e%2e%2fetc%2fpasswd
双URL编码对编码后的字符再编码一次,如%252e%252e%252f,若应用只解一次码则被绕过file=%252e%252e%252fetc%252fpasswd
路径截断(空字节注入)在路径末尾添加%00,使字符串在C语言层面截断,PHP < 5.3.4有效file=../../../etc/passwd%00
长路径溢出通过构建超长路径(如..\..\..\..\重复几百次),迫使应用截断处理file=..\..\..\..\..\..\..\..\..\..\windows\system32\config\SAM
Unicode/UTF-8变体利用..的Unicode表示(如..%c0%ae%c0%ae%c0%af)绕过基于ASCII的过滤file=..%c0%ae%c0%ae%c0%afetc%c0%afpasswd

以上手法中,URL编码和双编码是最常见的,因为许多开发者仅对../作简单字符串替换。路径截断在PHP 5.3.4之后已被修复,但仍存在于旧版本系统中。长路径溢出和Unicode变体则更多出现在对过滤规则不够严格的场景。

防御配置与代码实践

有效防御路径遍历漏洞需要从运行环境、代码逻辑和输入过滤三个层面协同实施。以下给出具体配置与代码示例。

1. 启用open_basedir限制文件访问范围

在php.ini或.user.ini中设置open_basedir,将PHP进程可访问的文件目录限制在特定路径内。例如:

open_basedir = /var/www/html:/tmp

此配置下,即使攻击者成功注入路径遍历,也无法访问/etc/passwd等系统文件。但需注意open_basedir对include()、fopen()等函数生效,对file_get_contents()等函数同样生效。

2. 使用realpath()规范化路径并校验

在代码中获取用户输入后,先通过realpath()函数将路径转换为绝对路径,然后检查该路径是否在以允许的根目录开头。示例如下:

\$allowedBase = '/var/www/html/uploads/';
\$userInput = \$_GET['file'];
\$fullPath = realpath(\$allowedBase . \$userInput);
if (\$fullPath === false || strpos(\$fullPath, \$allowedBase) !== 0) {
    die('Invalid file.');
}
include(\$fullPath);

此方法能有效对抗../和编码绕过,因为realpath()会解析所有符号链接和相对路径,返回规范化的绝对路径。但注意realpath()要求文件必须存在,对于不存在的文件会返回false,因此需先处理文件不存在的情况。

3. 禁止使用用户输入直接拼接路径

最佳实践是将用户输入映射到预定义的允许文件列表中,而非直接当作文件路径。例如:

\$allowedFiles = [
    'report1' => 'report_2024_q1.pdf',
    'report2' => 'report_2024_q2.pdf',
];
\$key = \$_GET['file'];
if (!isset(\$allowedFiles[\$key])) {
    die('Invalid file.');
}
\$filePath = '/var/www/html/uploads/' . \$allowedFiles[\$key];
readfile(\$filePath);

这种白名单方式从根本上杜绝了路径遍历,因为攻击者无法注入任意路径。

4. 输入过滤与编码校验

如果必须使用用户输入的部分路径,应严格过滤不可见字符和特殊符号。以下正则表达式仅允许字母、数字、下划线和点:

if (!preg_match('/^[a-zA-Z0-9_\.]+$/', \$_GET['file'])) {
    die('Invalid filename.');
}

同时,对输入进行URL解码两次(因为可能存在双编码),再检查是否包含../或..\。注意解码顺序:先解码一次,再解码一次,然后检查。但需注意rawurldecode()不会解码+号,但路径中+通常不相关。

从一次实际渗透测试看路径遍历漏洞的修复

在某次渗透测试中,发现一个用户头像下载功能:download.php?file=avatar_123.jpg。服务器端代码简单拼接了目录:\$file = '/var/www/html/avatars/' . \$_GET['file'];。测试人员使用file=../../../etc/passwd成功读取了/etc/passwd。修复方案如下:

  • 首先,在php.ini中设置open_basedir = /var/www/html,限制了PHP进程可访问范围。
  • 其次,修改代码为使用realpath()校验,并确保路径以/var/www/html/avatars/开头。
  • 最后,将用户输入改为基于ID的映射,不再直接使用文件名。

经再次测试,所有绕过手法均失效。该案例说明,综合防御措施比单一手段更可靠。

总结

PHP路径遍历漏洞的防御不能仅依赖输入过滤,因为攻击者总能找到编码变体。最有效的策略是“白名单+运行环境限制+规范化校验”的三层防御。通过open_basedir限制文件访问范围,使用realpath()规范化路径并校验,以及将用户输入映射到预定义文件列表,可以从根本上避免路径遍历漏洞的产生。开发者应定期进行代码审计,使用自动化工具扫描此类漏洞,并关注PHP官方安全更新。只有将安全编码规范融入开发流程,才能构建坚固的Web应用防线。

分享到:

评论

加载中...
我
还没有评论,快来抢沙发吧~