把 PHP 服务部署到 Ubuntu 上,很多人先关注的是能不能跑起来,真正上线后才发现安全问题更难补。与其零散地加几个模块,不如按“系统更新、PHP 配置、Web 服务器、权限与审计、代码实践”这几层来检查,这样更容易判断哪些措施是基础项,哪些属于针对业务场景的强化项。
下面这份清单保留了常用命令、配置项和目录示例,适合直接对照现有环境逐项核查。即使你暂时不一次性全部启用,也可以先把最容易落地、收益最高的部分补齐,尽快降低暴露面。
先把系统和 PHP 版本保持在可维护状态
PHP 安全的第一道防线,通常不是某个高级扩展,而是及时更新系统和运行时。大量攻击利用的都是已经公开、也已经修复的旧漏洞。
Ubuntu 环境中,建议定期执行以下命令更新系统组件:
sudo apt update && sudo apt upgrade
同时也要同步更新 PHP 本体和常用扩展,避免核心解释器与扩展库长期落后:
sudo apt install php php-cli php-fpm php-mysql php-curl php-xml php-zip
这一步的意义很直接:先把已知漏洞窗口缩到最小,再谈后面的细粒度加固。
重点检查 php.ini 里的高风险配置
很多 PHP 安全问题并不是代码写坏了,而是默认配置过宽。上线前,至少要把下面几类设置过一遍。

不要把报错信息直接暴露给用户
生产环境里,错误细节不应该直接回显到页面。否则路径、变量名、SQL 结构甚至扩展状态都可能被外部拿去分析。
display_errors = Off
log_errors = On
error_log = /var/log/php_errors.log
这样做的好处是把调试信息留在服务器内,既方便排查,也减少敏感信息泄露。
按业务情况禁用危险函数
如果应用并不依赖系统命令调用,就应该尽量收紧高风险函数。像 eval、exec、system、passthru、shell_exec、curl_exec 一旦被利用,往往会把问题从应用层直接带到系统层。
可以通过 disable_functions 统一禁用。这类限制尤其适合常规 CMS、后台系统、内容站和接口服务。
上传能力要同时限制大小和访问范围
文件上传不是简单地限制一个大小参数,更关键的是限制脚本可访问目录,避免上传点成为落地入口。
upload_max_filesize = 2M
post_max_size = 8M
open_basedir = /var/www/html/:/tmp/
upload_max_filesize = 2M 和 post_max_size = 8M 用来控制上传体积,open_basedir 则限制 PHP 只能访问指定路径,例如 /var/www/html/ 和 /tmp/。即使有恶意文件进入上传目录,活动范围也会被压缩。
关闭远程资源包含能力
远程文件包含攻击(RFI)之所以危险,就在于攻击者可以借助 URL 让脚本加载外部恶意代码。生产环境通常不需要保留这类能力。
allow_url_fopen = Off
allow_url_include = Off
这两项关闭后,PHP 通过 URL 直接加载远程资源的空间会明显变小。
OPcache 不只是性能优化
启用 OPcache 的常见理由是减少重复编译、提升响应速度,但它对运行稳定性也有帮助。脚本不必频繁重复解析,运行面会更收敛。
opcache.enable=1
它不是替代安全配置的手段,但适合作为生产环境的基础项保留。
Web 服务器要和 PHP 一起收紧
PHP 本身配得再严,如果 Apache 或 Nginx 暴露过多信息、脚本执行边界不清,整体风险依然很高。

Apache 环境重点看模块与信息暴露
Apache 场景下,常见的加固项包括启用 Web 应用防护模块、限制异常请求,以及减少响应头泄露的信息量。
sudo a2enmod security2
sudo a2enmod evasive20
mod_security 可以拦截 SQL 注入、XSS 等常见攻击流量,mod_evasive 适合对暴力请求和部分 DDoS 行为做基础缓解。
ServerTokens Prod
Header unset X-Powered-By
隐藏 Apache 版本信息、移除 X-Powered-By 响应头,虽然不直接修复漏洞,但能减少被动暴露。
Nginx 环境重点收紧脚本执行路径
Nginx 的核心思路是:只有明确允许的 PHP 入口才能交给 PHP-FPM 处理,其他位置即使出现脚本文件也不能执行。
location = /xmlrpc.php { deny all; }
这类规则适合直接封掉不需要的脚本入口。
location ~ .php$
需要确保只有这个 PHP 处理块把请求交给 PHP-FPM,而不是让任意目录里的脚本都有执行机会。
location ~ ^/(uploads|assets)/.*.(php|php5|jsp)$ { deny all; }
上传目录和静态资源目录通常只该存放文件,不该成为脚本执行点。这条规则就是为了防止“上传成功后直接执行”。
用系统安全模块和权限控制补上隔离层
到这一步,目标已经不只是“减少攻击入口”,还包括“即使某处被利用,影响范围也尽量受限”。
AppArmor 或 SELinux 用来限制 PHP 进程边界
AppArmor 和 SELinux 的价值在于进程级隔离。哪怕 PHP 服务被入侵,也不代表它能自由读取系统关键目录。
sudo aa-enforce /etc/apparmor.d/usr.sbin.php-fpm
通过 AppArmor 强制启用配置后,PHP-FPM 的访问范围会受到限制,不再能随意触达 /etc、/root 这类敏感位置。
setenforce 1
如果环境使用 SELinux,开启强制模式同样可以提高隔离强度。
Suhosin 适合作为额外防护层
Suhosin 常被视作 PHP 的补充安全扩展,适合在需要更严运行限制的环境中作为附加层使用。
sudo apt install php-suhosin
安装后,它可以提供缓冲区溢出防护、格式化串攻击防御等额外保护,用来补足 PHP 原生机制的空白。
文件与目录权限不要图省事
权限管理的原则很简单:只给必要权限,不给“万能权限”。
sudo chown -R www-data:www-data /path/to/php/project
先确保项目文件归属正确,避免不同用户混用造成越权写入。
sudo chmod -R ug+rwx storage bootstrap/cache
像 storage、bootstrap/cache 这类需要写入的目录,可以按需授予读写权限;而 chmod 777 这种做法应当避免,它会把控制面直接放大。
把传输、入口和监控补齐
很多环境前面的配置都做了,但在 HTTPS、防火墙和日志审计上留了空白,结果问题出现后既拦不住,也追不清。

HTTPS 是默认项,不是可选项
客户端与服务器之间如果没有 TLS 保护,中间人攻击、会话窃听和敏感数据泄露都会变得容易。
sudo apt install certbot python3-certbot-apache
sudo certbot --apache -d yourdomain.com
使用 Let's Encrypt 可以较低成本启用 SSL/TLS 证书,把站点流量切到 HTTPS。
只开放必要端口,并收紧 SSH 登录方式
服务器不该把所有入口都暴露在公网。对大多数 PHP 网站来说,通常只需要开放 80 和 443。
sudo ufw allow 'Nginx Full'
sudo ufw enable
配合 UFW,可以先把 Web 服务放行,再关闭其他不必要端口。
此外,建议修改 /etc/ssh/sshd_config,将 PermitRootLogin no 写入配置,并尽量使用密钥认证替代密码登录,降低 SSH 被撞库或暴力破解的概率。
日志和审计的作用是尽快发现异常
安全体系不能只有“防”,还必须有“看见问题”的能力。PHP 错误日志、Web 访问日志和文件变更审计,是最基础也最实用的三类信号。
tail -f /var/log/php_errors.log
这个命令适合实时观察异常输出,快速定位是否出现集中报错或异常请求触发。
sudo apt install auditd
sudo auditctl -w /path/to/php/files -p wa -k php-files
安装 auditd 后,可以对 PHP 文件的写入和修改行为做跟踪。一旦出现可疑落地文件、批量篡改或异常更新,定位会快很多。
代码层面的防护和后续维护不能省
系统和服务器可以缩小攻击面,但最终能不能守住,还是要看业务代码本身是否遵守基本的安全规则。
所有外部输入都要校验与转义
不要默认相信用户提交的数据。邮箱、URL 等格式可以先用 filter_var() 验证;输出到页面前,再根据场景使用 htmlspecialchars() 或 strip_tags() 做处理。
这属于防 XSS 的基本动作,越早统一到框架层或公共函数层,后续越不容易漏。
数据库访问必须使用预处理语句
SQL 注入防护最稳妥的方式,仍然是 PDO 或 MySQLi 的预处理语句,而不是手工拼接字符串。
$stmt = $mysqli->prepare("SELECT * FROM users WHERE id = ?"); $stmt->bind_param("i", $id);
这段示例的重点不在语法,而在边界:参数与 SQL 结构分离,数据库不会把输入直接当作语句的一部分执行。
密码存储使用 PHP 内置哈希 API
密码不要自行设计加密方案,直接使用 PHP 提供的密码哈希接口即可。
$hashed_password = password_hash('user_password', PASSWORD_DEFAULT);
校验时配合 password_verify() 使用,既省掉自实现风险,也更容易跟随 PHP 默认策略升级。
定期审计和备份,才能应对长期运行风险
安全加固不是做完一次配置就结束。代码会变、依赖会变、暴露面也会变,因此需要定期检查漏洞、保留回滚手段。
可以使用 PHPSA(PHP Security Advisories Checker)等工具扫描潜在安全问题,比如未过滤输入、过时函数或已知风险依赖。
备份也应纳入固定流程:
sudo rsync -a vz /var/www/html /path/to/backup
mysqldump -u username -p database_name > database_backup.sql
前者用于备份站点文件,后者用于备份数据库。真正遇到文件被篡改、版本回退或数据库异常时,恢复能力往往比单点防御更关键。







