网站被挂马、被植入后门,是很多个人站长深夜最怕遇到的事。我在给朋友的服务器做排查时见过太多类似的场景:页面突然被跳转到赌博网站,百度收录里出现一堆乱七八糟的页面,或者服务器 CPU 莫名其妙跑满,一查发现是被人种了挖矿程序。这些事故十有八九都跟 PHP 配置不当有关。很多站长装完 LNMP 环境之后,php.ini 从头到尾没动过,默认配置等于把大门敞开。这篇文章从 php.ini 出发,讲讲 PHP 安全加固里最关键的三个配置:disable_functions、open_basedir,以及上传安全,都是个人站长能上手就用的实战经验。
一、先关掉信息泄露的几个开关
在动大配置之前,先把两个泄露服务器信息的小开关关掉。第一个是 expose_php,默认 On 的时候,PHP 会在 HTTP 响应头里输出 X-Powered-By: PHP/版本号,等于告诉全世界你用的 PHP 版本。攻击者拿到版本号,就能去漏洞库里精准匹配这个版本的所有已知漏洞。第二个是 display_errors,生产环境一定要设为 Off,否则 SQL 报错、文件路径、数据库连接信息这些敏感内容会直接打在网页上,对攻击者来说就是送上门的地图。
; /etc/php/版本号/fpm/php.ini 里的关键配置
expose_php = Off
display_errors = Off
log_errors = On
error_reporting = E_ALL把错误显示关掉之后,建议把 log_errors 打开,错误信息记到日志文件里,自己排查问题时还能看,只是不展示给访问者。改完 php.ini 记得重启 PHP-FPM,配置才会生效:systemctl restart php-fpm 或者按你的系统服务名重启。
二、disable_functions:把危险函数关进笼子
disable_functions 是 PHP 安全加固里最实用的一项配置,作用是把指定的 PHP 函数全局禁用。对被入侵的网站来说,攻击者通常会上传一个一句话木马,然后用它执行系统命令、读写文件、连接外网。如果我们把执行这些操作的关键函数全部禁掉,即使木马被传上来了,也基本是个废品。
个人站长建议至少禁用下面这些函数:exec、shell_exec、system、passthru、popen、proc_open 这组是直接执行系统命令的;eval、assert 是动态执行代码的,一句话木马最常用;还有 file_put_contents、fopen、unlink 这类文件操作函数,如果网站程序确实用不到,也可以禁掉。配置写法如下:
disable_functions = exec,shell_exec,system,passthru,popen,proc_open,eval,assert,pcntl_exec这里有个前提要提醒:禁用函数之前,先确认你的网站程序没有用到。比如有些 PHP 程序用 exec 调用外部命令处理图片、压缩文件,禁掉之后功能会报错。稳妥的做法是先在测试环境把函数禁掉,跑一遍网站的核心流程,确认没问题再上生产。像 WordPress、Typecho 这类常规博客程序,禁用上面这些函数基本不影响正常使用。
三、open_basedir:把 PHP 关进指定目录
open_basedir 是 PHP 的目录访问限制,设置之后,PHP 脚本只能读取和写入指定目录内的文件,目录之外的任何文件操作都会被拒绝。这个配置的价值在于:即使攻击者通过任意文件读取漏洞拿到了路径,也读不到 /etc/passwd、网站配置里的数据库密码等敏感文件。
; 以网站目录为例,限制 PHP 只能访问网站目录和临时目录
open_basedir = /www/wwwroot/example.com/:/tmp/配置里多个目录用冒号分隔,注意 /tmp 一般要保留,因为 PHP 上传文件、会话数据都会用到临时目录,不加上会导致上传和登录功能异常。有的面板(比如宝塔)在站点设置里直接有 open_basedir 选项,勾选网站目录即可,不用手动改 php.ini。改完同样要重启 PHP-FPM 生效。
这里再介绍一个进阶技巧:如果服务器上跑了多个站点,每个站点的目录结构不一样,全局的 open_basedir 可能不够灵活,这时候可以用 .user.ini 文件。PHP 从 5.3 版本开始支持在网站根目录放一个 .user.ini,里面的配置会以 CGI 模式覆盖全局设置,相当于针对单个站点做定制。比如在网站根目录创建 .user.ini,写入 open_basedir = /www/wwwroot/example.com/:/tmp/,重启 PHP-FPM 后这个配置只对这个站点生效,不影响其他站点。要注意 .user.ini 的文件名以点开头,默认会被隐藏,内容同样属于敏感信息,权限设为 644 即可,不要放在能被 Web 下载的目录里。
设置了 open_basedir 之后,如果网站出现打不开文件、上传失败这类报错,多半是某个功能用到了限制目录之外的文件,对照报错里的路径,把对应目录加进白名单就行。
四、上传安全:从源头堵住木马
文件上传是 webshell 入侵的主要入口,个人站长必须认真对待。上传漏洞最常见的利用方式:攻击者上传一个后缀伪装成图片的 PHP 文件,配合解析漏洞让服务器把它当脚本执行。防守要分几层来做。
第一层,限制上传类型。代码层面严格校验文件后缀和 MIME 类型,只允许图片、文档这类白名单后缀。第二层,重命名文件。上传的文件不要保留用户原始文件名,用随机字符串重命名,并且固定后缀,比如时间戳加随机数再加 .jpg,让攻击者没法通过文件名做文章。第三层,文件存储和执行的隔离。上传目录和执行目录分开,上传的附件目录设置为禁止执行 PHP,Nginx 里加一条规则即可:location 匹配到上传目录时,对 .php 结尾的请求直接返回 403。
# Nginx 配置:uploads 目录禁止执行 PHP
location ~* ^/usr/uploads/.*\.(php|php5|phtml)$ {
deny all;
}另外,检查一下你的 PHP 上传配置:upload_max_filesize 和 post_max_size 按需设置,不要给得太大,一般 20M 以内足够;upload_tmp_dir 确认指向的目录权限正常,不要设置成 777 权限。
五、权限与目录最小化
PHP 安全还离不开文件系统权限的配合。网站目录的权限遵循最小化原则:文件 644、目录 755,只有真正需要写入的目录(上传目录、缓存目录、日志目录)才给写权限,而且写权限尽量给到运行用户,不要一股脑 777。很多挂马事故的根源就是整个网站目录 777,任何进程都能往里写文件,木马自然来去自如。
还有一个常见隐患是备份文件。很多站长习惯把备份的压缩包放在网站目录里,比如 backup.zip、wwwroot.tar.gz,如果路径被猜到,整个网站源码和数据库配置就全暴露了。备份文件务必放到网站根目录之外,或者加上访问鉴权。
六、加固后的自查清单
配置都做完之后,用一个简单的清单自查一遍:第一,phpinfo 页面已经无法访问,或者已经关闭信息输出;第二,在网站目录里放一个测试文件执行 system 命令,确认被禁止;第三,尝试读取网站目录之外的文件,确认被 open_basedir 拦截;第四,检查网站目录下没有可疑的 .php 文件,特别是上传目录和图片目录;第五,确认备份文件不在网站根目录下。这五条全部通过,你的 PHP 环境就算达到了个人站长级的加固水平。
安全加固不是一次性的工作,而是一个持续的过程。装了新插件、换了新程序,都要回头看一眼这些配置有没有被改动。另外定期查看访问日志里的可疑请求(比如 POST 到图片目录、请求带 eval 参数),配合防火墙把扫描 IP 拉黑,能把大部分攻击挡在门外。记住,防御的目的不是让攻击者进不来,而是让进来了的攻击者什么都干不了——disable_functions、open_basedir、上传隔离这三板斧,正是这个思路的核心。