PHP文件包含漏洞如何避免

wen PHP项目 2

本文目录导读:

PHP文件包含漏洞如何避免

  1. 代码层防御(最核心)
  2. 配置加固(php.ini)
  3. 架构与业务层面
  4. 高级进阶:危险函数替代方案
  5. 终极防御:WAF 与监控
  6. 安全检查清单

PHP 文件包含漏洞(如本地文件包含 LFI 和远程文件包含 RFI)是 Web 安全中非常危险的漏洞,攻击者可利用它读取敏感文件、执行恶意代码,甚至完全控制服务器。

要彻底避免这类漏洞,需要从编码规范、配置加固、架构思想三个层面进行防御,以下是全面的防护方案:

代码层防御(最核心)

这是最直接、最有效的防线,核心原则是永远不要信任用户输入

A. 严格的输入验证(白名单机制)

永远不要直接拼接用户输入到文件路径中,而是使用白名单进行映射。

  • 错误做法

    $page = $_GET['page'];
    include($page . '.php'); // 攻击者传入 ../../etc/passwd
  • 正确做法(映射表)

    $allowed_pages = [
        'home' => 'home.php',
        'about' => 'about.php',
        'contact' => 'contact.php'
    ];
    $page = $_GET['page'] ?? 'home';
    // 检查键是否存在,如果存在,只允许取对应的值
    if (isset($allowed_pages[$page])) {
        include(__DIR__ . '/pages/' . $allowed_pages[$page]);
    } else {
        // 返回默认页或404错误
        include(__DIR__ . '/pages/error.php');
    }

B. 绝对路径与路径规范化(Path Canonicalization)

如果需要动态指定路径,禁止使用相对路径(),必须转换为绝对路径,并在包含前校验真实路径。

$base_dir = '/var/www/html/secure/pages/';
$path = $base_dir . basename($_GET['file']); // basename() 去掉路径中的目录部分
// 关键:使用 realpath() 解析并检查最终的绝对路径是否在允许的目录内
$real_path = realpath($path);
if ($real_path !== false && strpos($real_path, $base_dir) === 0) {
    include($real_path);
} else {
    die('Invalid file path.');
}

C. 禁用动态包含(PHP 8.0+ 建议)

如果包含的文件名是固定的,不要使用变量,如果非要使用,检查 allow_url_include(见下方配置)。


配置加固(php.ini)

修改 PHP 配置文件,降低被利用的风险(LFI 和 RFI 都适用)。

  • 禁止远程文件包含(RFI):这是最重要的一条。

    ; 默认是 Off,确保是 Off
    allow_url_include = Off
  • 禁止开启远程文件打开(针对某些扩展)

    ; 不建议开启,防止通过 URL 读取文件
    allow_url_fopen = Off
  • 关闭危险函数(如果业务不需要):在 disable_functions 中禁用可能导致文件读写的函数,但注意这会连带影响正常业务。

    disable_functions = exec, system, shell_exec, passthru, popen, proc_open
  • 敏感文件防护:禁止 PHP 解析某些敏感后缀(如 .txt, .log),防止包含日志文件时执行代码。

    ; 在 Nginx/Apache 配置中,对 uploads 目录禁止执行 PHP
    ; Nginx:
    location ~ \.php$ { deny all; }

架构与业务层面

A. 目录权限与隔离

  • 目录权限:存放模板或包含文件的目录,其权限设为只读(755),确保 Web 服务器无法写入。
  • 上传目录隔离:用户上传文件的目录严禁存放 PHP 文件,且严禁通过 include 直接包含上传目录的文件。

B. 日志文件包含防护

攻击者常通过把恶意代码写入日志(如 User-Agent),然后通过 LFI 包含日志文件执行代码。

  • 防护方法
    1. 日志目录放在 Web 根目录之外
    2. 如果必须在根目录内,给日志文件设置只读权限(chmod 640),并禁止 Web 服务器对该目录执行 PHP。

C. 使用框架自带的模板引擎

如果业务复杂,建议使用成熟的 PHP 框架(如 Laravel, Symfony)或模板引擎(Twig, Smarty)。

  • 框架通常自带 view()render() 方法,内部已经封装了严格的文件路径校验和输入过滤,不要手动拼接 include

高级进阶:危险函数替代方案

在某些极端情况下(如业务必须解析用户指定的文件),请使用以下替代方案:

  • 避免使用 include/require:它们会将文件当作 PHP 代码执行,如果只是读取内容,使用 file_get_contents()fopen(),并配合 htmlspecialchars() 输出,避免 XSS。
  • 使用 readfile() 输出文件内容,但同样要先做白名单过滤。

终极防御:WAF 与监控

  • Web 应用防火墙(WAF):部署 WAF 规则,拦截带有 , %00(Null字节,已废弃),php://filterdata:// 等特征参数的请求。
  • IDS/IPS:监控服务器日志,发现异常的路径穿越尝试立即告警。

安全检查清单

检查项 状态 操作
allow_url_include Off 强制关闭 RFI
包含路径来源 不使用 $_GET/$_POST 直接拼接 改用白名单映射
路径校验 使用 realpath()strpos 验证 阻止目录穿越
危险函数 检查 disable_functions 禁用 exec,system
上传目录 禁止执行 PHP 隔离上传与执行
日志文件 绝对拒绝包含 防止日志投毒

最重要的一句话:任何用户可控的输入,都不应该直接或间接地决定 includerequire 的文件路径。 如果可以,尽量使用路由+控制器+模板映射的方式取代动态文件包含。

抱歉,评论功能暂时关闭!