open_basedir是php强制生效的路径边界线,必须路径末尾加斜杠、多路径用冒号分隔、配置后重启php服务才生效;它仅限制文件操作函数,对system等命令函数无效,需配合禁用危险函数使用。

open_basedir 是 PHP 的核心安全机制,不是开关,而是强制生效的路径边界线。它限制 include、require、file_get_contents、fopen 等文件操作函数只能访问指定目录及其子目录——设错会直接报错(如 open_basedir restriction in effect),设漏会导致框架无法加载核心文件,设宽了又等于没设。
关键就三点:路径必须带末尾斜杠 /、多路径用英文冒号 : 分隔(Linux)、改完必须重启 PHP 服务。
open_basedir 路径怎么写才对?
❌ 错误写法:
/www/wwwroot/myapp
→ PHP 会把它当字符串前缀匹配,/www/wwwroot/myapp_bak/config.php也能被读取,存在绕过风险。✅ 正确写法:
/www/wwwroot/myapp/:/tmp/:/proc/
→ 每个路径结尾加/,表示精确目录;多个路径用:连接;/tmp/和/proc/必须包含,否则上传、日志、session 全部失败。-
ThinkPHP/Laravel 类框架常见结构是:
/www/wwwroot/myapp/ ├── public/ ← 网站入口(DOCUMENT_ROOT) └── thinkphp/ ← 核心代码在上层
所以
open_basedir至少要包含:-
public/(Web 可访问目录) - 项目根目录(如
/www/wwwroot/myapp/,用于加载vendor/、thinkphp/等) - 临时目录(
/tmp/,session、缓存、上传临时文件依赖)
-
哪里设置最推荐?优先级从高到低
-
首选
.user.ini(宝塔/LNMP 默认)- 文件位置:网站根目录(如
/www/wwwroot/myapp/.user.ini) - 编辑内容:
open_basedir=/www/wwwroot/myapp/public/:/www/wwwroot/myapp/:/tmp/:/proc/
- 保存后,在宝塔 PHP 设置页点“重载配置”或“重启 PHP-FPM”
- ⚠️ 若提示“无法保存”,先执行
chattr -i .user.ini解锁,保存后再chattr +i .user.ini锁定
- 文件位置:网站根目录(如
-
次选 Nginx fastcgi_param(加固用)
- 在站点配置或
fastcgi.conf中加入:fastcgi_param PHP_VALUE "open_basedir=/www/wwwroot/myapp/public/:/www/wwwroot/myapp/:/tmp/:/proc/";
- 修改后需
nginx -s reload或重启 Nginx - 优势:优先级高于
.user.ini,可防部分绕过行为
- 在站点配置或
-
慎用 php.ini 全局设置
- 适合单站环境;多站共用时,一个站点配宽了,其他站就暴露了
- 修改后必须重启 PHP-FPM(仅 reload 不生效)
- 示例:
[HOST=myapp.example.com] open_basedir = /www/wwwroot/myapp/public/:/www/wwwroot/myapp/:/tmp/:/proc/
怎么验证设置生效了?
别猜,直接测:
-
创建一个 PHP 文件(如
info.php),写入:<?php echo ini_get('open_basedir');浏览器访问,输出应和你配置的一致。
-
再试越界访问:
<?php file_get_contents('/etc/passwd');如果返回
open_basedir restriction in effect,说明生效;如果返回内容或权限错误,说明没起作用。
注意:ini_set('open_basedir', ...) 在 FastCGI 模式下基本无效,且多数生产环境已禁用该函数。
配置时容易忽略的细节
- 符号链接(软链)部署时(如
current → releases/20260730),open_basedir检查的是真实物理路径,不是链接路径,务必按realpath()结果来配。 - Composer 项目要额外确认
vendor/是否在允许路径内,否则require vendor/autoload.php会失败。 -
open_basedir不管system()、exec()等命令执行函数,必须配合disable_functions使用才完整。
不复杂但容易忽略。











