PHP服务器安全审计:90%高危漏洞藏于配置末行
|
去年1月4号下午三点零七分,运维把告警截图甩进群的时候,我正蹲在测试机前等一个 curl 响应——那台联想 ThinkStation P320,i7-8700K,内存条插槽三号松了,每次重装 PHP 模块都得先把机箱侧盖卸下来按两下。告警是 php-fpm 子进程崩溃堆栈,第五层调用栈停在 spl_autoload_register 的回调里,但真正崩的不是 autoload,是 /etc/php/7.4/fpm/pool.d/www.conf 末行那个多敲的空格加回车。
文章配图,仅供参考 对,就那行:slowlog = /var/log/php7.4-fpm-slow.log 后面跟了个不可见的 \\ ,没配 chown,也没加 chmod 644。php-fpm master 进程启动时读配置全量 parse,到最后一行,lexer 把换行后的空白字符喂给了 open() 系统调用——于是它真去 open 了 `/var/log/php7.4-fpm-slow.log` 后面那个“空路径”,也就是当前工作目录。然后 fpm 就以 root 权限,在 /etc/php/7.4/fpm/pool.d/ 下建了个叫 “” 的空名文件夹(Linux 允许,别笑)。再往后,所有子进程 fork 出来,cwd 都被污染成这个无名目录。结果你猜怎么着?当某个 POST 请求触发了 session_start(),session.save_path 默认是 /var/lib/php/sessions,但此时 realpath() 返回的是 /etc/php/7.4/fpm/pool.d//var/lib/php/sessions ——双斜杠中间那个空字符串,刚好让 open_basedir 的路径检查失效了。漏洞链闭合:任意文件写入 → session 文件可控 → RCE。白板上我们贴过这张攻击路径图,左下角还沾着半块干掉的咖啡渍,没人擦。 会议室的白板没擦干净,上面还留着三天前和产品吵架时画的接口流程图,箭头全指向一个写着“先跑通”的圆角矩形。那天他拿着 iPad 在后排划拉,说“这个导出功能用户明天就要试用”,我敲完最后一行 ini_set('display_errors', 'Off') 打包发版,顺手删掉了部署脚本里 grep -q "expose_php.Off" 的校验逻辑——因为上周刚帮他们修过另一套环境,那台机器的 php.ini 末行是 expose_php = Off 后面带了个中文全角空格,导致 apache2ctl configtest 一直报 Parse error: unexpected T_STRING,查了六小时。键盘上有个键掉了,是 Caps Lock,胶帽在抽屉第三格,跟半包受潮的薄荷糖混在一起。 这种事根本不用归因。不是 PHP 语言问题,也不是配置管理工具不成熟——你让 Ansible 去 diff 一个文本末行的空白字符?它连 \ 控制台报错永远不提这个。它只说“Failed to write session data”,或者“Cannot load module”,或者直接 segfault。性能面板的一行 call stack 也永远不展开 parser.c 的 yyparse,它只在 bottom-up 归约失败时吐一句“unexpected token”,token 是什么?是个不可见的 '\\0',还是个被 lexer 当成 comment 开头的 ';'?没人 debug 到这层。有人端着杯子站了很久,水汽在杯壁凝成一圈,像极了那段没加 semicolon 的 declare(strict_types=1); 后面那个被 IDE 忽略的 BOM 头。 我试过自动化扫描——写了 37 行 Python 去遍历所有 .ini、.conf、.htaccess,校验每行末尾的空白字符和行结束符;跑了两天,发现漏掉了 /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini,因为它是通过 volume mount 进容器的,权限是 777,而我们的扫描脚本默认跳过非 root 所有者目录。后来干脆改用 strace -e trace=openat php -m,看它到底在 open 哪些路径——结果第一眼就看到它试图 open("/etc/php/7.4/fpm/pool.d/\ 外卖到了没人去拿,袋子在门边堆了四份,其中一份汤洒了,在地板上洇出不规则地图。我撕开泡面包装时盯着锅里翻滚的油花想:PHP 解析器从不拒绝空行,但空行之后如果跟着一个被忽略的注释标记,或者一个未闭合的引号,或者一个没有 owner 的目录路径,整个解析上下文就垮了。垮得悄无声息,像上次线上 session 全丢,我们查 redis key TTL,查 nginx upstream 状态,查 mysql binlog 时间戳,就是没打开 /etc/php/7.4/fpm/pool.d/www.conf,用 hexdump -C 看最后一行。 我现在每天上线前,会 ssh 到灰度机,cd /etc/php//fpm/pool.d/,然后 run:for i in .conf; do echo "== $i =="; tail -n2 "$i" | hexdump -C; done。不是信不过 CI,是信不过我自己——昨天我本地改完 opcache.revalidate_freq,保存时按了 Cmd+S 又下意识补了个 Enter。工位的显示器是竖着的,方便扫长日志,但它也让我更容易错过一行只有两个空格的致命尾巴。 接下来三天,我打算把所有生产环境 pool.d 下的 .conf 文件做一次 tr -d '\ (编辑:站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |


PHP转新能源小程序,半年月入12万!
VR工程师转战PHP:三天性能调优实战
PHP工程师必避的5大服务器选型陷阱与调优实战
Ruby老兵看PHP电商开发者悄然转向三大新技术
多媒体服务器安全审计:90%漏洞源于3个被忽视配置
服务器安全审计:90%漏洞源于3类配置疏漏
PHP架构师私藏调试心法:3行代码秒定90%线上故障

