每个WordPress站点必须用独立系统用户运行,通过创建无登录权限用户、设置文件属主与权限、配置独立PHP-FPM池及open_basedir限制实现真隔离,禁用宝塔批量功能以防覆盖。

宝塔面板里每个WordPress站点必须用独立系统用户运行
默认情况下,宝塔所有网站都跑在 www 用户下,一个 WP 被黑,其他全沦陷。真隔离的第一步,是让每个站点的 PHP 进程、文件属主、日志写入全部归属到各自专属系统用户,而不是共用 www。
操作路径:宝塔 → 网站 → 选中某个站点 → 设置 → 网站目录 → 右上角「权限设置」→ 关闭「PHP 运行环境继承父级」→ 手动指定「运行用户」为新建的独立用户(如 wp_a123)。
- 必须提前用
useradd -s /sbin/nologin -M wp_a123创建无登录权限的系统用户(-M防止创建家目录,避免冗余) - 站点根目录(如
/www/wwwroot/site1.example.com)需chown -R wp_a123:www,组保留www是为了让 Nginx 仍可读取静态资源 - PHP-FPM 池配置会被宝塔自动重写,但你要确认生成的
/www/server/php/82/etc/php-fpm.d/wp_a123.conf里user = wp_a123和group = www正确
WordPress 核心文件与上传目录的权限要分层控制
很多用户以为改了运行用户就安全了,结果 wp-content 下的 plugins 或 uploads 仍能被任意 WP 通过 PHP file_get_contents 读取——因为 Linux 权限只管属主/属组/其他,不防同组内跨目录访问。必须靠“属主唯一 + 组权限收紧 + 上传目录额外限制”三重卡死。
-
wp-config.php必须chmod 600且属主为wp_a123,防止被其他站点的 PHP 用include加载 -
wp-content整体属主设为wp_a123:wp_a123,然后chmod 750;这样同属www组的其他用户无法进入 -
wp-content/uploads需单独处理:属主保持wp_a123:www,chmod 755(Nginx 要读),但加open_basedir限制——在站点的 PHP 设置 → 配置修改 → 在php.ini末尾追加:open_basedir = /www/wwwroot/site1.example.com/:/tmp/:/proc/
宝塔批量操作几十个站点时,别信「一键同步」和「模板应用」
宝塔的「网站模板」或「批量设置」功能会强行统一运行用户和权限,直接废掉隔离。几十个站点必须用脚本驱动,否则漏配一个就等于留后门。
- 先用
ls /www/wwwroot/列出所有站点目录名,生成用户列表(如site1.example.com → wp_site1) - 写 Bash 脚本循环执行:
useradd -s /sbin/nologin -M wp_site1、chown -R wp_site1:www /www/wwwroot/site1.example.com、chmod 600 /www/wwwroot/site1.example.com/wp-config.php - PHP-FPM 池不能依赖宝塔自动生成——手动复制一份基础池配置,替换其中
user/listen(建议用端口区分,如127.0.0.1:9011),再用ln -sf软链到php-fpm.d/目录,避免宝塔重启时覆盖 - 最后逐个进宝塔后台,确认「网站」→「设置」→「PHP版本」里选的是你刚配好的独立池(名称含
wp_site1),不是全局的php-fpm
验证隔离是否生效的关键检查点
做完所有配置,别只看宝塔界面显示“运行用户已修改”。真实攻击面在 PHP 进程能否跨站读写,必须用实际代码验证。
- 在站点 A 的
wp-content/themes/twentytwentyfour/functions.php末尾加:<?php @file_put_contents('/www/wwwroot/site2.example.com/test_cross.txt', 'hacked');,访问 A 站,检查 B 站目录下是否真的生成了文件(应该失败) - 在站点 B 的页面里用
print_r(scandir('/www/wwwroot/site1.example.com/wp-content'));,应报错Permission denied或返回空数组 - 用
ps aux | grep php-fpm确认每个池进程的 USER 列确实是对应独立用户(不是全显示www) - 检查
/proc/$(pgrep -f 'php-fpm: pool wp_site1')/status | grep Uid,第一行 UID 应为该用户的实际数字 ID,不是 0 或 1000
最易被忽略的是 open_basedir 的路径结尾斜杠——写成 /www/wwwroot/site1.example.com(缺斜杠)会导致子目录绕过;还有宝塔升级后自动重写 PHP-FPM 配置,得把关键池配置加 # BT_DISABLE 注释头防覆盖。


















