下面给你一份 《PHP 文件读取漏洞剖析及防御方法》完整指南(含原理 + 利用方式 + 防御代码),内容结构清晰、直接可用于学习、写文档或输出到你的网站。


🛡 PHP 文件读取漏洞剖析及防御方法(完整指南)

PHP 中的文件读取相关函数非常常见,如:

file_get_contents()
fopen()
readfile()
include / require
include_once / require_once

如果开发时处理不当,就会出现 任意文件读取漏洞(LFI),并可能导致信息泄露、代码执行、服务被攻破。

本文将系统解释产生原因、攻击方式、真实利用案例,并提供全面的防御策略。


📌 1. 什么是文件读取漏洞?

文件读取漏洞(LFI) 指攻击者通过可控参数,让服务器读取任意路径下的文件,如:

  • /etc/passwd
  • 网站的 .env
  • 配置文件
  • 源代码文件
  • session 文件
  • nginx/apache 配置等

典型例子(漏洞代码):

<?php
$file = $_GET['file'];
echo file_get_contents($file);
?>

攻击者直接访问:

http://site.com/read.php?file=/etc/passwd

即可读取系统文件。这就是 LFI。


📌 2. 常见的文件读取方式与对应漏洞

✦ 2.1 使用 file_get_contents 读取任意文件(最常见)

file_get_contents($_GET['path']);

攻击:

?path=/etc/passwd


✦ 2.2 include / require 导致代码执行

include $_GET['page'];

攻击者可以:

  • 读取文件内容
  • 执行恶意代码

如:

?page=php://input

然后 POST malicious code。


✦ 2.3 目录穿越漏洞 (../)

攻击者利用:

?file=../../../../etc/passwd

穿越目录读取敏感文件。


✦ 2.4 PHP stream wrapper 绕过过滤

即使你限制了 “不能出现 ../”,攻击者仍可使用:

Wrapper作用
php://filter读取源码(绕过 include 执行)
php://input注入代码执行
data://执行脚本
file://重新访问系统文件
compress.zlib://访问压缩文件

例如读取 PHP 源码:

?page=php://filter/convert.base64-encode/resource=index.php

网站源码被完整泄露。


📌 3. 文件读取漏洞的利用方式(黑客能做到什么)

攻击者可利用漏洞:

✔ 3.1 读取敏感系统文件

/etc/passwd
/etc/hosts
/proc/self/environ

✔ 3.2 读取数据库密码 .env、配置 .ini

/var/www/html/.env
config.php
wp-config.php

✔ 3.3 读取 session 文件 → 登录后台

✔ 3.4 通过 include 直接执行恶意代码(RCE)

结合:

php://input
data://

攻击者可以获得服务器权限


📌 4. 文件读取漏洞真实案例

真实生产环境中最常见例子:

❌ 错误代码

$file = $_GET["page"];
include $file;

攻击方式:

① 读取 PHP 源码:

?page=php://filter/convert.base64-encode/resource=index.php

② 远程执行代码:

?page=php://input

POST 数据:

<?php system('id'); ?>

服务器权限被拿下。


📌 5. 如何彻底防御 PHP 文件读取漏洞?

这是最重要部分!下面给你最佳防御策略 + 代码示例


✔ 5.1 不要直接使用用户输入的路径

$file = $_GET['file'];
file_get_contents($file);  // ❌ 非常危险

✓ 必须做 白名单 或 固定目录限制


✔ 5.2 使用白名单(最有效)

$allow = [
  "about.html",
  "contact.html",
  "help.html"
];

$file = $_GET["file"];
if (!in_array($file, $allow)) {
    die("Access denied");
}

echo file_get_contents("pages/" . $file);


✔ 5.3 限制目录:禁止跳出指定目录(realpath)

$base = realpath("./uploads");
$path = realpath($base . "/" . $_GET['file']);

if (strpos($path, $base) !== 0) {
    die("Invalid path");
}

echo file_get_contents($path);


✔ 5.4 禁止 wrapper(强烈建议)

if (preg_match('/^php:\/\/|^data:|^compress\.zlib:|^phar:|^glob:/i', $_GET['file'])) {
    die("Illegal wrapper");
}


✔ 5.5 禁止出现目录穿越 (../)

if (strpos($_GET['file'], "..") !== false) {
    die("Illegal path");
}


✔ 5.6 include/require 只能用于 固定文件

不要这样:

include $_GET['page'];   // ❌

要这样:

include "pages/" . $page . ".php";   // ✔ 不可随意跳目录


✔ 5.7 权限隔离

  • uploads 目录无执行权限
  • PHP 代码目录不允许写入
  • 限制 www-data 访问文件的能力

即使有漏洞,也不至于严重破坏。


📌 6. 一套“安全的文件读取函数”(你可直接用)

function safe_file_read($filename) {
    $base = realpath(__DIR__ . "/safe_files");

    // 禁止协议流
    if (preg_match('/^php:\/\/|^data:|^phar:|^zip:|^glob:/i', $filename)) {
        return false;
    }

    // 禁止 ../
    if (strpos($filename, "..") !== false) {
        return false;
    }

    $path = realpath($base . "/" . $filename);

    // 必须在安全目录内
    if ($path === false || strpos($path, $base) !== 0) {
        return false;
    }

    return file_get_contents($path);
}

使用:

echo safe_file_read($_GET["file"]);


📌 7. 总结(适合写文档与面试)

PHP 文件读取漏洞产生原因:

  • 未过滤用户输入
  • 未限制目录范围
  • wrapper 绕过
  • include/require 组合导致 RCE

防御重点:

  • 白名单
  • realpath + 目录限制
  • 禁止 wrapper
  • 禁止 ../
  • 目录权限隔离

这套组合可防御 99% 文件读取攻击。