PHP虚拟目录安全核心是三端协同:open_basedir必须启用且路径以斜杠结尾防前缀绕过;用户输入须urldecode+basename+白名单校验+realpath比对授权根目录;Nginx需精确拦截上传目录PHP执行并严格设置属主与权限。

PHP 创建虚拟目录本身不直接产生安全风险,真正危险的是配置不当导致的路径解析失控——比如用用户输入拼接路径、未限制脚本可访问范围、上传目录允许执行 PHP 文件等。防目录遍历不是加一层判断就能解决的事,而是要从环境、代码、服务三端同步收紧。
open_basedir 必须启用且路径结尾带斜杠
这是 PHP 层最硬的边界。不启用等于把文件系统地图交给攻击者;启用但路径写成 /var/www/html(缺尾部斜杠)会触发前缀匹配漏洞,/var/www/html_evil 也能绕过。
- 正确写法示例:/var/www/html/public/:/var/www/html/:/tmp/:/proc/
- 多路径用英文冒号分隔,中间不能有空格、中文或换行
- 在 .user.ini 中设置时,先
chattr -i .user.ini解锁,保存后再chattr +i .user.ini锁定防篡改 - 验证是否生效:新建
info.php,调用ini_get('open_basedir'),输出必须是非空字符串
用户输入路径必须剥离+校验+比对真实路径
任何来自 URL、表单、API 的路径参数都不能直接拼进 fopen() 或 file_put_contents()。攻击者传 ../../etc/passwd 或 %2e%2e%2fconfig.php,PHP 不会自动拦截。
- 先
urldecode()再用basename()提取纯文件名,丢掉所有路径符号 - 再用白名单正则过滤,例如:
preg_match('/^[a-zA-Z0-9_-]{1,64}$/', $filename),拒绝点号、斜杠、空字节 - 构造完整目标路径后,必须调用
realpath($target),并确认它以realpath(UPLOAD_ROOT)开头(注意用 !== 0 判断) -
UPLOAD_ROOT必须是绝对路径,且不能由用户输入动态拼出
Nginx 层禁执行上传目录(双保险)
仅靠 PHP 层 open_basedir 和代码过滤不够。攻击者可能上传 shell.php 后直接请求 /upload/shell.php,绕过所有应用逻辑。
立即学习“PHP免费学习笔记(深入)”;
- 在站点 Nginx 配置的
location块中添加精确拦截规则: location ~ ^/upload/.*\.(php|php5|phtml|phar|sh|pl|py|jsp|asp|aspx|cgi|exe)$ { deny all; }- 注意 ^/upload/ 是锚定开头,防止
/xxx/upload/类路径绕过 - 扩展名列表必须覆盖常见 WebShell 后缀,尤其别漏掉 phtml 和 phar(PHP 8.0 默认启用 phar 扩展)
- 该规则必须放在
location ~ \.php$之前,否则会被优先匹配到 fastcgi_pass
目录权限与属主必须按角色严格分配
755 不等于安全,关键是谁拥有它。如果目录属主是 root,而 PHP-FPM 运行用户是 www-data,那 755 的“其他用户”权限就完全失效——www-data 实际只有“其他用户”权限,连读都失败。
- Web 根目录(如 /var/www/html)属主设为 www-data,权限 755
- 上传目录(如 /var/www/html/upload)同样属主 www-data,但权限建议 750(组可读执行,其它用户无任何权限)
- 敏感配置文件(如 .env)权限必须是 600 或 640,且确保不在 Web 可访问路径下
- 禁止将
allow_url_fopen = On,防止通过php://filter等协议读取或写入任意文件



















