上传目录:只当静态文件用,任何 PHP 一律拒绝
结论先行:防跨站的核心只有两句话——用 open_basedir 把 PHP 进程能"看见"的文件锁在站点目录以内,用目录权限把"运行用户能改"的范围压到最小。只做其中一个都会留缺口:光有 open_basedir,被拿下的站仍可能改自己目录里的代码;光有权限控制,一个站照样能读走隔壁站的数据库配置。
威胁模型:跨站不是"跨域名",是"跨目录"
同一台服务器上多个站点共用一套 PHP-FPM 时,如果每个站点都用同一个运行用户、且 PHP 没有目录限制,那么 A 站被上传一个 webshell,就等于拿到了整台机器的文件读写权——读 B 站的 config、往 C 站的 uploads 写马,全都能做。
所以防跨站的落点是两条:一是限制 PHP 进程的文件系统视野(open_basedir),二是限制运行用户的可写路径(目录权限)。前者是"看不见",后者是"改不动"。
open_basedir:给 PHP 画一个圈
结论:open_basedir 要写在 FPM 的 pool 配置里,用 php_admin_value,这样用户代码无法用 ini_set 自己放开限制。
具体做法,在站点的 pool 配置(宝塔一般是 `/www/server/php/74/etc/php-fpm.d/站点名.conf`)里加:
php_admin_value[open_basedir] = /www/wwwroot/site.com:/tmp
几个必须注意的点:
- 分隔符是冒号,Linux 下用 `:`,写成逗号或分号会直接失效(分号是 Windows 的写法)。
- 一定要带上临时目录。不写 `/tmp`,上传、session、临时文件写入就会报错。Clara BBS 的图片上传依赖 PHP 的临时文件落地,这类站点漏掉临时目录几乎是必炸。
- 它按真实路径解析。站点目录里放个软链接指向圈外,一样会被拒绝。
- 报错关键词是 `open_basedir restriction in effect`,通常伴随 `failed to open stream`。看到这个就知道是圈画小了,往列表里补路径即可,不要急着去改权限。
- 宝塔面板里"网站 → 网站目录 → 防跨站攻击"这个开关,底层做的就是这件事(写 `.user.ini` 或 pool 配置)。手动改也行,但别两边同时改出不一致的路径。
目录权限:能写的地方越少越好
结论:目录 755、文件 644、属主统一为 PHP 运行用户(宝塔通常是 www),然后只把运行时的可写目录单独放开。
一条可复制的批量命令:
cd /www/wwwroot/site.com
chown -R www:www .
find . -type d -exec chmod 755 {} \;
find . -type f -exec chmod 644 {} \;
chmod -R 775 uploads cache logs # 只留这几个可写
为什么这么配:644/755 已经足够让 www 用户读取和执行,只有真正需要写盘的目录才给 `775`。这样即使攻击者拿到了 PHP 执行权,也写不进核心代码目录,只能污染 uploads——而 uploads 里的 PHP 文件我们下一步会在 Nginx 层直接掐掉。
注意两点:
- 可写目录必须真的可写。Clara BBS 官方排查"图片上传失败"的第一条就提到 `uploads` 目录需可写,压权限时别一刀切。
- 需要后台上传插件的系统要留口子。Clara BBS 的插件放在 `content/plugins`,运行时钩子加载、保存即生效,如果站点需要在线装插件,这个目录得留给运行用户;如果不需要,就一并锁死更安全。
Nginx 层补刀:上传目录不许解析 PHP
结论:`location` 匹配优先级里 `^~` 前缀匹配优先于正则,用它把上传目录单独圈出来禁掉 PHP 解析,是最省事的写法。
location ^~ /uploads/ {
location ~ \.php$ { return 403; }
}
# 隐藏文件、配置、版本库一律不可访问
location ~ /\.(?!well-known) { deny all; }
另外,Clara BBS 这类系统自动识别 `.html` 后缀 URL,伪静态只需要把非静态请求转发到 `index.php`(Nginx 用 `try_files`,Apache 用 `.htaccess`),不需要额外规则——规则越多越容易写漏,反而不安全。
怎么验证配好了
结论:用"访问圈外文件"和"往圈外写文件"两个动作就能验完,不用猜。
在站点根目录放一个临时 php 文件,尝试 `file_get_contents('/etc/passwd')` 与向 `/www/wwwroot/别的站/` 写文件:前者应报 open_basedir 限制,后者应报 Permission denied。两项都符合预期,再删掉探针文件。
排错顺序固定为:先看 PHP 错误日志里的关键词 → 是 open_basedir 就补路径,是 permission denied 就查属主和权限位 → 最后才怀疑 Nginx 规则。
说到底,open_basedir 管的是"能看见什么",目录权限管的是"能改什么",Nginx 规则管的是"上传目录不许执行"。三件事各管一段,组合起来才叫防跨站,单拉出任何一个都不算配完。
转载请注明出处,版权归原作者所有。
星耀SVIP
管理员
黑卡会员





