本文目录导读:

- 为什么 PHP 需要 AppArmor?
- AppArmor 核心概念:Profile、模式与进程标签
- 为 PHP-FPM 生成专属 Profile
- 如何调试与修复被拦截的 PHP 文件操作
- 实战问答:常见配置错误与性能影响分析
- 扩展技巧:与 SELinux 对比及迁移注意事项
**
《PHP 环境加固实战:AppArmor 强制访问控制完整配置指南》
目录导读
- 为什么 PHP 需要 AppArmor?——从安全漏洞说起
- AppArmor 核心概念:Profile、模式与进程标签
- 为 PHP-FPM 生成专属 Profile(附命令行实操)
- 如何调试与修复被拦截的 PHP 文件操作(aa-log 分析)
- 实战问答:常见配置错误与性能影响分析
- 扩展技巧:与 SELinux 对比及迁移注意事项
为什么 PHP 需要 AppArmor?
PHP 作为动态语言,常被用于处理上传文件、执行系统命令或读写临时目录,若 Web 应用存在远程代码执行(RCE)漏洞,攻击者可能通过 system() 或 include 函数渗透服务器,AppArmor(Application Armor)是 Linux 内核内置的强制访问控制(MAC)模块,能限制程序仅访问预设的特定文件、网络端口或目录,即使 PHP 进程被攻破,也无法越权读取 /etc/shadow 或写入 Web 根目录以外的区域。
与传统的 chmod 权限不同,AppArmor 基于“程序路径”而非“用户身份”进行限制,特别适合共享主机环境,一个 PHP 脚本试图读取 /var/www/private 下的文件,即便运行用户是 www-data,若 Profile 未授权,内核会直接返回 Permission denied。
AppArmor 核心概念:Profile、模式与进程标签
- Profile:定义特定程序(如
/usr/sbin/php-fpm8.1)允许的操作集合,语法类似path flags。 - 模式:
enforce(强制拦截)与complain(仅记录日志,放行操作),生产环境建议先使用 complain 模式测试。 - 进程标签:通过
aa-status命令查看当前活跃的 Profile,PHP-FPM 通常挂载为php-fpm标签。
检查系统是否已启用 AppArmor:
sudo aa-status
若输出 apparmor module is loaded,则可继续操作。
为 PHP-FPM 生成专属 Profile
步骤 1:安装工具链
sudo apt install apparmor-utils
步骤 2:生成基础 Profile(自动侦测)
sudo aa-genprof /usr/sbin/php-fpm8.1
程序会进入交互模式,此时需启动一些 PHP 请求以生成日志,用 curl 访问站点任意页面,触发文件读取操作。
步骤 3:手动补充关键路径规则
编辑生成的 /etc/apparmor.d/usr.sbin.php-fpm8.1,在 profile 块内添加:
/var/www/html/ r, /var/www/html/** rw, /tmp/php*.sock rw, /var/log/php-fpm.log w, network inet stream,
/** rw 允许递归读写,但要注意开放过度,建议将上传目录单独设置为 rw,其他目录设为只读 r。
步骤 4:重新加载并强制执行
sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.php-fpm8.1 sudo aa-enforce /usr/sbin/php-fpm8.1
使用 aa-complain 可临时切换回日志模式以便调试。
如何调试与修复被拦截的 PHP 文件操作
当 PHP 抛出不寻常的 readfile() 或 mkdir() 失败时,查看内核日志:
sudo dmesg | tail -n 20 grep 'apparmor' /var/log/syslog
典型错误示例如下:
[ 1234.56] audit: type=1400 audit(...): apparmor="DENIED" operation="open" profile="php-fpm" name="/var/www/site/tmp/backup.zip" pid=1234 comm="php-fpm"
根据 name 字段,编辑 Profile 并在对应目录后追加权限:
/var/www/site/tmp/** rw,
保存后重新加载,若拦截来自 PHP 的 exec() 调用,需添加 ix 或 px 规则允许执行特定二进制文件(如 /usr/bin/curl):
/usr/bin/curl ix,
实战问答:常见配置错误与性能影响分析
问:AppArmor 会影响 PHP-FPM 性能吗?
答:影响极小,AppArmor 通过 LSM 钩子检查访问,每次文件操作增加约 0.1ms 开销,对于高并发场景可忽略不计,但注意,若 Profile 规则过于复杂(如数百条正则),会略微增加 CPU 占用。
问:为什么我的 PHP 脚本能读取 /etc/passwd 但仍提示权限错误?
答:请检查 Profile 是否处于 complain 模式,使用 sudo aa-status 确认状态,若显示 complain,则不会真正拦截,但日志会记录,需要执行 aa-enforce 才生效。
问:如何允许 PHP 连接外部 MySQL 数据库?
答:需添加网络访问规则,在 Profile 中加入:
network inet tcp, network inet udp,
更严格的限定可指定端口:network inet tcp # 3306(注意 AppArmor 不支持端口限定,需通过 capability 或 net 域实现,此处建议直接允许 TCP)。
问:部署后 WordPress 上传图片失败,如何处理?
答:检查上传目录写入权限,在 Profile 中添加:
/var/www/wordpress/wp-content/uploads/ rw,
并使用 aa-log 工具实时监控:
sudo tail -f /var/log/syslog | grep apparmor
扩展技巧:与 SELinux 对比及迁移注意事项
- SELinux 使用标签(如
httpd_sys_script_t)管理,复杂度高但粒度更细;AppArmor 基于路径,更易理解。 - 若从 SELinux 迁移,需注意 AppArmor 不区分“文件类型”(如管道、套接字),仅通过
r、w标记。 - 在容器环境(Docker)中,默认不加载 AppArmor,需通过
--security-opt apparmor=php-profile启用。
最后提示:在正式环境应用前,建议在 staging 环境开启 complain 模式运行 72 小时,通过 aa-log 汇总所有被拦截操作,一次性补齐权限规则,避免反复弹错。