加入收藏 | 设为首页 | 会员中心 | 我要投稿 站长网 (https://www.1461.cn/)- 应用程序、AI行业应用、CDN、低代码、区块链!
当前位置: 首页 > 服务器 > 安全 > 正文

PHP服务器安全审计:90%高危漏洞藏于配置末行

发布时间:2026-11-04 14:10:10 所属栏目:安全 来源:DaWei
导读:  跟老板拍桌子那天是去年寒假,腊月二十三,小年。我盯着他工位上那杯放凉的枸杞茶——浮着三颗蔫了的枸杞,杯沿有半个牙印,应该是他自己咬的。我说PHP安全审计90%高危漏洞藏在配置末行,他手指头在桌面敲了三下,像在等我改

  跟老板拍桌子那天是去年寒假,腊月二十三,小年。我盯着他工位上那杯放凉的枸杞茶——浮着三颗蔫了的枸杞,杯沿有半个牙印,应该是他自己咬的。我说PHP安全审计90%高危漏洞藏在配置末行,他手指头在桌面敲了三下,像在等我改口。我没改。他抓起保温杯起身去接水,后端老张正好推门进来,手里捏着一张A4纸,边角卷得能当小喇叭吹,上面全是php.ini的diff patch,但第一页就漏了open_basedir后面那个换行符——对,就是那个空行之后的disable_functions=exec,passthru,system,shell_exec——它被抄错了位置,落在了[PATH]段底下,没生效。我原以为是注释符号吃掉了它,结果grep -n “disable_functions”一跑,发现它压根没进ini parser,因为前一行末尾多了一个\
\
\
(Windows写入的换行混进Linux环境)。这个我敢打包票,光是查这个就干掉我两天半,连Lighthouse跑分截图发到群里没人回——你猜怎么着?前端小林在群里甩了个“已阅”,然后立刻@我问能不能把font-display: swap改成optional,再没提过截图的事。


  首屏救火?不,那是假动作。真救火是半夜三点收到告警:/wp-content/plugins/xxx/phpinfo.php被扫出,IP段全是巴西的VPS,但webshell路径却指向/home/www/xxxx.com/public/index.php.bak——这玩意儿半年前就被我rm -rf了。最后扒出来是php-fpm的slowlog里一条记录:config file path /etc/php/8.1/fpm/php.ini (not loaded),底下紧跟着(loaded from /etc/php/8.1/fpm/conf.d/99-custom.ini)。99-custom.ini最后一行是:expose_php = Off<span style="color:#f00"><br></span>(对,HTML实体br被硬生生写进去了);它没报错,因为ini parser默认跳过非法键值对,但它让整个文件后续的disable_functions全失效。所以phpinfo()照样吐版本号,system()照样调,就卡在那一行末尾带了个看不见的
。你说巧不巧?去年寒假,我在机房对着KVM控制台盯了六小时,就为了确认是不是console log颜色骗了我——结果不是,是vim的listchars开了tab:»·,但没开trail:,导致末行空格根本看不见。桌面那个没关的录屏还挂着,窗口右上角时间显示15:23,下午三点零七分,咖啡杯底结了一圈浅褐色的环。


  PM上周五又提了个需求:“能不能让接口返回更快点?用户等三秒就不耐烦了。”我反手给他发了份nginx access log,里面有一千八百条200响应,TTFB全在四十七毫秒上下——比他手机微信冷启动还快。他看都没看,直接转发给测试组说“看看是不是缓存没配好”。我说缓存没问题,是php.ini里realpath_cache_size=4096太小,但重启fpm之前必须先清realpath_cache_ttl,不然新配置根本进不去内核级缓存区——这事我跟产品吵过三次,最后一次他在会议室白板上画了个树状图,说“咱们得从用户心智模型出发”。我默默掏出手机,把他画的图截了屏,发给运维同事老陈,配文:“帮忙查下这棵心智树,是不是根目录权限777。”他回了个“?”和一个充电线表情——就是那种红蓝双色编织线,对面工程师上个月刚换的,说旧的充到78%就断连。


  优胜劣汰是真的。不是玄学,是物理性淘汰。谁还在php.ini里用#号注释?早该滚了。谁还手写extension=redis.so而不是用dpkg自动注入conf.d?也快了。现在真正活着的配置,都是靠CI流水线自动生成、带SHA256校验、末行强制加空行、最后执行sed -i ‘$a\\’保证换行结尾的。可问题是——我们产线还有十一台PHP服务器是去年年初上线的,维护人叫小王,离职前交接文档里写了“所有配置按模板走”,结果他复制的模板里,末行是\
\
\

文章配图,仅供参考

\
。我删了两行,第三行留着——因为第四行才是真正的disable_functions。这种事没法复盘,复盘就是认怂。CDN账单上上个月多刷了十八万,全是爬虫打穿了未鉴权的.phpinfo暴露面,而那个phpinfo是某次热更新时,dev分支误合进master的,就因为merge请求里勾选“合并前自动格式化”、再勾选“保留空白行”、再勾选“应用pre-commit hook”——这三个选项我亲眼看着点了三遍,手抖了一下,第三个没点上。


  无所谓。反正我下周调去支撑Java组,那边说他们jvm.options末行要是少个回车,JVM直接不启。听起来更刺激。但我还是得把现有这三台FPM池里的max_children调回来——昨天发现被设成了128,而宿主机只有2核4G。这个我说不准,可能是自动扩缩容脚本写的bug,也可能是有人拿ansible playbook覆盖时忘了改模板变量。对了,倒水的是前台小姑娘,穿灰毛衣,左袖口脱了线,水壶放在她桌上,壶嘴朝西。我顺手把录屏关了,关之前看到时间停在15:23:04——刚好是告警触发的时刻。


  现在我要去把/usr/local/bin/php-check-config这个脚本再塞两行:第一行检查每一行末尾是不是\
(且仅\
),第二行如果发现\
\
或\
就直接exit 1并写syslog。别笑,真的,我已经见过七次,都是同一类错误——不是人懒,是编辑器、传输协议、Git auto-crlf、scp挂载点顺序……乱成一团。下次再遇到,我不查日志了,直接ssh上去vi +$ php.ini,光标停在最后一行,盯十秒。要是眼睛酸了还没看出问题——那就删掉最后一行,重新输一遍,回车,:wq。就这么简单。当然,前提是别让后端老张在旁边念叨:“要不咱试试把disable_functions改成disable_fuctions?”他说这话的时候正拧开一瓶冰镇茉莉花茶,瓶盖弹到我键盘F12键上,咔嗒一声。

(编辑:站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!