白名单映射是防文件包含漏洞最可靠方式,需硬编码、语义化键名、绝对路径、isset校验、正则过滤键名,并配合open_basedir和allow_url_include=off等环境配置。

直接用白名单映射代替动态拼接,是目前最可靠、最易落地的防文件包含漏洞方式。其他手段(如过滤、正则、basename)都存在绕过可能,而白名单从逻辑上切断了用户输入到文件路径的直连关系。
白名单数组必须硬编码,不能从配置文件或数据库读取
白名单若来自外部可变源(如JSON配置、MySQL表、.env变量),攻击者一旦控制该数据源,整个防御就失效。硬编码确保映射关系在代码审查阶段即可锁定。
-
$pages = ['home' => '/var/www/app/views/home.php', 'about' => '/var/www/app/views/about.php'];是安全的 -
$pages = json_decode(file_get_contents('/config/pages.json'), true);是危险的 —— 文件可能被覆盖或注入 - 键名应使用语义化短标识(如
'user_profile'),而非原始文件名(如'profile.php'),避免暴露路径结构 - 值必须是**绝对路径**,且需提前验证存在性:
file_exists($pages[$key]) && is_readable($pages[$key])
接收参数后只做键查找,绝不拼接字符串
常见错误是把用户输入当路径片段再拼进 include,哪怕加了 basename() 也挡不住空字节截断或双写编码绕过。白名单的核心就是“查表”,不是“清洗”。
- 正确做法:
$page_id = $_GET['p'] ?? ''; if (!isset($pages[$page_id])) { http_response_code(403); exit; } include $pages[$page_id]; - 错误做法:
include '/views/' . basename($_GET['p']) . '.php';—— 即使basename()也拦不住xxx%00.php - 不要用
in_array()查找键;必须用isset()或array_key_exists(),否则会因类型转换误判(如0 == '0') - 对键名做最小化校验:仅允许小写字母、下划线、数字,长度限制在 2–32 字符,可用
preg_match('/^[a-z0-9_]{2,32}$/', $page_id)快速兜底
白名单路径需配合 open_basedir 和 allow_url_include=Off
白名单本身不解决环境层风险。如果 allow_url_include 开着,攻击者仍可通过 php://input 或远程 URL 绕过白名单;如果 open_basedir 未设,白名单里的绝对路径可能指向不该访问的系统目录。
- 必须在
php.ini中设置:allow_url_include = Off和allow_url_fopen = Off -
open_basedir应明确限定为应用根目录 + 临时目录:open_basedir = /var/www/app/:/tmp/(注意末尾斜杠) - 白名单中所有路径必须落在
open_basedir范围内,否则include会失败并报错Warning: include(): open_basedir restriction in effect - 上线前用
ini_get('open_basedir')和ini_get('allow_url_include')验证配置已生效
白名单映射真正难的不是写代码,而是维护——每次新增页面都要同步修改数组和路由逻辑。一旦疏忽漏加,就会出现 403 却找不到原因的情况。建议把白名单定义抽成独立 pages.php 文件,并加入自动单元测试,检查每个键是否对应真实可读文件。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











