禁用 allow_url_include 和 allow_url_fopen 是防御 rfi/lfi 的硬性底线;代码层须用白名单键值映射+realpath()校验+绝对路径前缀比对,禁用黑名单过滤与运行时 ini_set()。

禁用 allow_url_include 是防御 PHP 远程文件包含(RFI)最硬、最不可妥协的安全底线。它不是“可选加固项”,而是只要开启,其他所有代码层过滤都可能失效的开关。
php.ini 必须关闭 allow_url_include 和 allow_url_fopen
这两个配置共同构成 RFI/LFI 的基础执行条件:
-
allow_url_include = Off:直接禁止
include()、require()等函数加载http://、ftp://或php://等协议资源; -
allow_url_fopen = Off:虽不直触 include,但能阻断
file_get_contents()、fopen()等函数的远程读取能力,防止伪协议(如php://filter)和 SSRF 链路被利用。
修改后必须重启 PHP-FPM 或 Apache/Nginx,ini_set() 在运行时无效。验证方式:php -i | grep allow_url —— 输出应为 off 或无结果;或在脚本中调用 ini_get('allow_url_include') 返回空或 false。
代码层杜绝动态拼接,只走硬编码白名单
即使 allow_url_include 已关,本地文件包含(LFI)仍可通过 ../ 遍历读取敏感文件(如 /etc/passwd、日志、session)。因此,用户输入绝不能直接参与路径拼接:
- 禁止写法:
include($_GET['page'] . '.php')、require($dir . '/' . $_POST['cfg']); - 正确做法:定义硬编码关联数组,如
$pages = ['home' => 'pages/home.php', 'user' => 'modules/user/profile.php']; - 仅用用户输入作键名:
$key = $_GET['page'] ?? 'home'; if (!isset($pages[$key])) die('400');; - 路径拼接使用
__DIR__或dirname(__FILE__),不依赖可能被伪造的$_SERVER['DOCUMENT_ROOT']。
路径校验必须用 realpath() + 目录前缀比对
仅靠 basename() 或正则替换 ../ 完全不可靠,攻击者可用 ..%2f、.///、%00、双写等方式绕过:
- 先判断文件是否存在:
if (!file_exists($candidate)) { die(); }; - 再获取规范绝对路径:
$real = realpath($candidate);; - 严格比对是否位于预期目录内:
if (strpos($real, __DIR__ . '/pages/') !== 0) { die('Access denied'); }; - 避免使用
open_basedir当主防线——它易被绕过,且在 Composer、多租户等场景下易引发兼容性问题。
别碰黑名单过滤和“看起来安全”的清洗
以下做法已被实战反复证明无效:
- 用
str_replace(['http://', 'https://'], '', $input)—— 攻击者可用大小写、URL 编码、混淆协议(hxxp://)绕过; - 仅检查后缀是否为
.php—— 白名单里已含扩展名,拼接后缀反而引入风险; - 信任
parse_url()提取 host 后做模糊匹配 ——evil-example.com可命中example.com; - 把安全寄托在
open_basedir上 —— 它无法阻止同一受限目录内的恶意读取(如 session 文件、error_log)。
真正可靠的路径控制,只有白名单查键 + realpath() 校验 + 绝对路径前缀强制约束。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











