PHP 安全加固实战:Web 应用运行环境防护配置指南

PHP 安全加固是 Web 服务器防护的基础环节,本文从 php.ini 关键参数、危险函数禁用、目录访问限制、上传与会话安全等维度,提供一套可直接复制的 PHP 安全加固完整配置方案,帮助站长显著降低网站被入侵风险。

适用场景

  • 使用 PHP 搭建的网站(WordPress、ThinkPHP、Laravel 等)上线前的环境安全配置
  • 服务器被挂马后,重建 PHP 运行环境时的安全基线设置
  • 使用宝塔面板、LNMP 等一键环境后的安全参数复核
  • 等保测评、安全自查中的 Web 中间件加固项

前置条件

  • 已安装 PHP 的 Linux 服务器(本教程以 PHP 8.x 为例,兼容 7.x)
  • 具备 root 权限,可修改 php.ini 与 Web 服务器配置(Nginx/Apache)
  • 修改配置后需重启 PHP-FPM 生效

原理说明

PHP 安全加固的核心思路是最小权限原则:通过 php.ini 禁用攻击者可能滥用的函数、限制脚本可访问的文件目录范围、关闭不必要的危险特性,即使攻击者拿到了 WebShell 或存在文件包含漏洞,也无法轻易执行命令、读写敏感文件或横向移动。常见入侵链条中,evalsystem 等函数正是 WebShell 得以执行的关键节点。

操作步骤

1. 定位 php.ini 并备份

# 查看 PHP 加载的配置文件路径
php --ini

# 查看 PHP-FPM 进程加载的配置
php -i | grep -i "Loaded Configuration"

# 备份原配置
cp /etc/php/8.1/fpm/php.ini /etc/php/8.1/fpm/php.ini.bak

2. 禁用危险函数

编辑 php.ini,设置 disable_functions。以下是 Web 业务一般用不到但常被攻击者利用的高危函数清单:

disable_functions = passthru,exec,system,chroot,chgrp,chown,shell_exec,proc_open,popen,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,stream_socket_server,pcntl_exec,putenv,mail,error_log,apache_setenv

注意:exec、system、shell_exec、proc_open 是命令执行类 WebShell 最常调用的函数,必须禁用;若业务代码确实需要(如计划任务、ffmpeg 转码),可通过独立 PHP-CLI 环境或白名单方式单独放行。

3. 限制目录访问范围

; 限制 PHP 只能访问网站根目录与系统临时目录
open_basedir = /www/wwwroot/example.com:/tmp

open_basedir 生效后,脚本访问目录范围之外的任何文件都会报错,可有效阻断 WebShell 读取 /etc/passwd、数据库配置等敏感文件。

4. 关闭危险特性与错误信息

; 关闭错误回显,避免泄露路径、代码结构等信息
display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /var/log/php-fpm/php_errors.log

; 关闭允许包含远程文件(防止远程文件包含 RFI 攻击)
allow_url_include = Off

; 关闭文件上传的临时目录可执行权限依赖(按需)
file_uploads = On
upload_max_filesize = 20M
post_max_size = 25M

生产环境必须 display_errors = Off,配合 log_errors = On 将错误写入日志,既防信息泄露又不影响问题排查。

5. 会话与 Cookie 安全

; 会话 Cookie 仅通过 HTTPS 传输
session.cookie_secure = 1
; 禁止 JavaScript 读取会话 Cookie,缓解 XSS 窃取会话
session.cookie_httponly = 1
; 启用 SameSite 属性,缓解 CSRF
session.cookie_samesite = Lax
; 限制会话 ID 来源,防止会话固定攻击
session.use_strict_mode = 1

6. 上传目录禁止执行 PHP

仅改 php.ini 不够,还需在 Web 服务器层面禁止上传目录执行脚本。Nginx 配置示例:

location ~* ^/(upload|uploads|images|attach)/.*\.(php|php5|phtml)$ {
    deny all;
}

这条规则确保即使攻击者绕过了文件上传校验,也无法在图片目录直接执行上传的 PHP 文件。

7. 重启 PHP-FPM 使配置生效

# 检查配置语法
php-fpm8.1 -t

# 重启 PHP-FPM
sudo systemctl restart php8.1-fpm

# 验证是否生效
php -i | grep -E "disable_functions|open_basedir|display_errors"

配置验证

编写一个临时测试脚本验证加固效果:

# 创建测试脚本
cat > /www/wwwroot/example.com/security_test.php <<'EOF'
<?php
echo "disable_functions 测试:
";
var_dump(function_exists('system'));
echo "目录限制测试:
";
var_dump(@file_get_contents('/etc/passwd'));
echo "错误显示测试:
";
echo $undefined_var;
EOF

访问该脚本应看到:function_exists('system') 返回 false;读取 /etc/passwd 返回 false 或警告;未定义变量不输出错误详情。验证完成后立即删除测试脚本

常见问题

FAQ 1:禁用 exec 后网站某些功能报错怎么办?

先确认报错来源:用 grep -r "exec(" 网站目录 搜索业务代码中是否调用了被禁函数。若是核心功能依赖(如图片处理、队列任务),建议:一是将这些调用迁移到独立 PHP-CLI 脚本由计划任务执行;二是只放行必要函数如 exec 并配合 open_basedir 约束;三是改用无需 shell 的纯 PHP 扩展(如 Imagick 代替 ffmpeg 命令)。

FAQ 2:设置 open_basedir 后网站白屏/500 错误?

通常是网站需要访问的目录未加入白名单,如 Composer 缓存目录、临时目录、Session 目录等。查看 PHP 错误日志定位被拦截的路径,将这些目录追加到 open_basedir 中(用冒号分隔),例如 open_basedir = /www/wwwroot/example.com:/tmp:/www/wwwroot/example.com/runtime。建议逐步追加并持续观察日志,不要一次性删掉所有限制。

总结

PHP 安全加固不需要复杂设备,把 php.ini 的高危函数禁用、目录访问限制、错误信息隐藏、会话安全四项基础做扎实,就能阻断绝大多数 WebShell 与注入类攻击。配合 Web 服务器层的上传目录禁执行规则,可形成应用层与中间件层的双重防护。加固完成后建议使用 WPScan、Nmap、WebShell 查杀工具进行一次完整复查,确保加固没有引入业务故障,也没有遗漏安全死角。