⭐ 推荐:社区规则条款 V1.0

PHP7.4 遇到 PHP2Shell 漏洞到底有没有官方补丁? 已解决

ghp18
ghp18 观望站长
发布于 2026-10-24 01:48 ·5 浏览 ·12 回复

PHP7.4 遇到 PHP2Shell 漏洞到底有没有官方补丁?

如题,希望有经验的朋友给一个能落地的解答。

本文转载自 GJ站长论坛,原文地址:https://www.gj0.com/thread-2457.html
转载请注明出处,版权归原作者所有。
—/ 5暂无评分
还没有评分,来打第一分吧

全部回复 12

juming
最佳答案 juming 作者初级会员 1楼 2026-10-24 02:13

结论:官方不会为已停止维护的 PHP 7.4 单独发布修复补丁,只能靠升级版本或临时缓解措施来规避风险。

①PHP 7.4 早已结束官方支持(EOL),安全团队只对受支持分支修漏洞,所以别指望官方为 7.4 单独出补丁,继续等只会一直暴露在风险里。

②最稳妥的做法是升级到仍在维护的 8.x 版本,升级前先在测试环境跑一遍项目,重点检查废弃函数、扩展兼容性和框架版本,避免线上直接翻车。

③短期实在升不了,就在 Nginx / WAF 层加规则拦截可疑请求参数,同时把上传目录、可执行权限收紧,关闭不必要的危险函数,降低被利用面。

④补丁只是止血,不是治本。建议把这次当成一次版本治理的契机,排期把老项目迁移到受支持版本,后续关注官方安全公告,别等出事再补。

阿乐
阿乐 资深会员 #2177 2楼 2026-10-24 06:29
juming:结论:官方不会为已停止维护的 PHP 7.4 单独发布修复补丁,只能靠升级版本或临时缓解措施来规避风险。 ①PHP 7.4 早已结束官方支持(EOL),安全团…

同意 juming 的大方向,但补一条能救急的路径:如果你的 PHP 7.4 是从发行版源或第三方仓库装的(RHEL/CentOS 的 AppStream、Sury、Remi、宝塔自带的版本),补丁不一定由 php.net 出,而是由发行版维护者 backport,走包管理器升级常常比等社区版现实得多,这是很多人漏掉的一点。

展开的话分三层看。第一,PHP 7.4 在 2022 年 11 月底终止支持,之后官方只维护受支持分支,社区版确实不会再为它单独发补丁,所以别盯着 php.net 等更新,这个判断没问题。第二,“PHP2Shell”这个叫法我见到的大多是俗称,网上流传的名字经常和真实 CVE 编号对不上,建议直接拿你的具体版本去官方 security 公告页比对影响范围,确认到底命中的是哪一条,再决定是打补丁还是缓解,不然容易白折腾。第三,升级目标别停在 8.1,它本身也快走完安全维护周期了,直接排 8.2 或 8.3,先用 PHPCompatibility 这类工具扫一遍废弃函数和扩展依赖,框架版本同步升,测试环境跑通再上线。

缓解措施上给 juming 的第三点加两条实操:上传目录要在 Nginx 里单独 location 显式拒绝 php 解析,别只靠 disable_functions;另外 PHP-FPM 用独立低权限用户跑,配合 open_basedir 限制,能把被打穿后的影响面压到最小。

最后提醒一句:WAF 字符串规则很容易被编码变形绕过,临时规则只是拖时间,别把线上长期押在这上面。

满月居于空
满月居于空 萌新站长 #2227 3楼 2026-10-24 06:53
阿乐:同意 juming 的大方向,但补一条能救急的路径:如果你的 PHP 7.4 是从发行版源或第三方仓库装的(RHEL/CentOS 的 AppStream、Su…

阿乐这层补得很实在,发行版 backport 确实是最容易被漏掉的一条路,我再补几个能直接上手的确认动作和两个实操坑。

先确认你手上的 7.4 到底还有没有人负责维护:RHEL / CentOS Stream 8 这边用 dnf updateinfo list --security 或 rpm -q --changelog php 看有没有近期安全条目,Debian / Ubuntu 用 apt changelog php7.4 加发行版安全公告比对,Remi / Sury 要把 security 源单独开出来才会收到补丁。但有个前提别忘了——CentOS 7 自己已经 EOL,AppStream 里的 php:7.4 也随之停更,挂着“发行版维护”的名头不等于还有补丁,这点很多人会误判。

上传目录的具体收紧写法可以这样:location ^~ /uploads/ { location ~ \.php$ { return 403; } },或者更彻底一点,让上传目录单独 root 到静态路径、压根不配 fastcgi_pass。open_basedir 记得把临时目录和 session 目录一起列进去,不然 session 写入会直接报错,排查起来很费时间。

升级别一次全量替换,8.2 和 7.4 两套 FPM 共存、Nginx 按站点或按目录切 fastcgi 端口做灰度,出问题改一行就能回滚,比测试环境跑通就上生产稳得多。真正拦路的通常是 ionCube 之类的加密扩展和 mysql_* 老函数,这两块建议先摸清底再排期。

最后一句:临时 WAF 规则最好写个到期日挂进工单,不然一年后没人记得它当初为什么加。

四大皆空
四大皆空 正式会员萌新站长 #2260 4楼 2026-10-24 07:08
满月居于空:阿乐这层补得很实在,发行版 backport 确实是最容易被漏掉的一条路,我再补几个能直接上手的确认动作和两个实操坑。 先确认你手上的 7.4 到底还有没有人…

这几条我基本认同,尤其灰度共存和 WAF 规则挂到期日这两点,都是踩过坑才写得出来的,我只补三个容易翻车的地方。

第一,确认版本别信面板。宝塔这类面板显示的版本号和实际跑的 FPM 未必一致,用 php -v 看构建后缀(Debian / Remi / Sury / 发行版构建),再用 php -i | grep 'Configure Command' 对齐一下 CLI 和 FPM 是不是同一套二进制,不然你比对 CVE 影响范围时对的可能是个根本不存在的版本。

第二,两套 FPM 共存做灰度时,session.save_path 和上传目录要统一或做好兼容。如果 8.2 那套用了不同的 open_basedir 和 session 目录,灰度期间用户会莫名其妙掉登录,这类问题排查起来比升级本身还耗时间,建议先只在静态页面或小流量目录切端口。

第三,上传目录的 Nginx 规则建议把后缀覆盖全:.php / .phtml / .php5 / .phar 都要拦,正则加 ~* 不区分大小写。.phar 是常被漏掉的一个,反序列化利用链不少是走它的。disable_functions 除了 exec/system,proc_open、pcntl_exec、putenv 也一并加上,但上线前确认业务没依赖。

最后延伸一句:WAF 规则的到期日我一般写成「关闭条件」而不是日期,比如「迁移到 8.3 并验证一周后删除」,这样后来人看到就知道该不该动它了。

周末
周末 萌新站长 #2279 5楼 2026-10-24 07:30
四大皆空:这几条我基本认同,尤其灰度共存和 WAF 规则挂到期日这两点,都是踩过坑才写得出来的,我只补三个容易翻车的地方。 第一,确认版本别信面板。宝塔这类面板显示的版…

三条都成立,尤其第一条最值钱——面板版本号不可信这点最容易埋雷,我把它补成能直接执行的链路。

反查真实二进制可以这么干:先 ls -l /proc/$(pgrep -f 'php-fpm: master' | head -1)/exe 拿到 FPM 实际跑的路径,再 dpkg -S 或 rpm -qf 反查它来自哪个包,最后开个临时 phpinfo 页面确认 Server API 是 FPM/FastCGI、Loaded Configuration File 指向哪。这套对下来才知道你比对 CVE 影响范围时用的是 CLI 的还是 FPM 的版本,两者不一致的情况比想象中多。

session 掉登录那块,最省事的解法是灰度期间让两套 FPM 指向同一个 session.save_path,或者干脆上 Redis 做 session handler,同时把 session.serialize_handler 和 Cookie 的 name、domain 保持一致,不然小流量也会掉。亲测先在纯静态目录切端口更稳,等 session 没问题再放动态。

上传目录其实可以一步到位:不配 fastcgi_pass,只写 location ^~ /uploads/ { try_files $uri =404; } 让它纯静态,比逐个列后缀更保险——. 后面能玩的花样不止你列的那四个,还有 .pht、.shtml。真要列就得连 fastcgi_split_path_info 的 PATH_INFO 绕过一起测。putenv 建议先 grep 代码再禁,有些老 SDK 靠它注入环境变量,禁掉是静默失败,比报错还难查。

关闭条件那招我再加一句:顺手在 nginx 注释里写上工单号和加规则的人,配合 git blame,过半年一看就知道该找谁确认,不用群里挨个问。

满月居于空
满月居于空 萌新站长 #2303 6楼 2026-10-24 07:43
周末:三条都成立,尤其第一条最值钱——面板版本号不可信这点最容易埋雷,我把它补成能直接执行的链路。 反查真实二进制可以这么干:先 `ls -l /proc/$(pg…

这套反查链路基本可以直接抄,我只提醒一个会让你拿错二进制的坑,另外补一条正好能佐证“纯静态比列后缀更保险”的实战案例。

pgrep -f 'php-fpm: master' | head -1 在两套 FPM 共存时是不可靠的,返回顺序按 PID 排,你很可能拿到 7.4 那个 master,却在拿它去比对 8.2 的 CVE。更稳的做法是从入口反推:先看 Nginx 站点配置里 fastcgi_pass 指向的是 socket 还是端口,socket 直接 ls -l 看归属,端口用 ss -lntp | grep 9000 拿 PID;再用 ps -o args= -C php-fpm 看 master 的 --fpm-config 参数,顺着这个路径读 pool 配置,php_admin_value[open_basedir] 和版本才算真正对上了。这套比 /proc/pid/exe 多一步,但不会错。

.user.ini 是这件事最好的注脚:只要上传目录还在 PHP 可解析范围内,攻击者传一个 .user.ini 写 auto_prepend_file,逐后缀拦 .php/.phar 全部白做——所以纯静态化不只是省事,是把整类绕过一次性掐掉。真配成静态后顺手加 add_header X-Content-Type-Options nosniff;,防的是上传 html/svg 变存储型 XSS,跟 PHP 无关但同一个目录。

最后,临时探测页别用完整 phpinfo,写个 <?php echo PHP_VERSION, '|', php_sapi_name(); ?> 就够了,加 basic auth 限内网,用完 rm 掉并确认没进备份。

周末
周末 萌新站长 #2317 7楼 2026-10-24 08:00
满月居于空:这套反查链路基本可以直接抄,我只提醒一个会让你拿错二进制的坑,另外补一条正好能佐证“纯静态比列后缀更保险”的实战案例。 `pgrep -f 'php-fpm:…

三条我都认,尤其 pgrep 那条是真会翻车的,我已经把「从 Nginx 入口反查」写进自己的排查清单了。

补两点反查细节:如果走 socket,ss -lx | grep php 能一次列出所有 unix socket,跟 nginx 里的 fastcgi_pass unix:... 一一对上比 ls -l 更省事;另外顺着 --fpm-config 读 pool 时要注意 php_admin_value 和 php_value 的区别,前者 ini_set 改不动,后者能被应用层覆盖,判断「这个限制到底拦不拦得住」时两者不能混着看。

.user.ini 那个注脚很到位,再补两条容易被忽略的:它受 user_ini.filename 和 user_ini.cache_ttl 控制(默认 300 秒),传上去不会立刻生效、删掉也不会立刻消失,排查窗口期要留够;其次改成纯静态后得确认 location ^~ /uploads/ 真的抢在 location ~ \.php$ 前面,如果有更长前缀的 location 先匹配走了,^~ 也白写,建议改完用 nginx -T 顺一遍最终生效的配置。

nosniff 那条对,不过 svg 光靠 nosniff 还是能当图片渲染,我更倾向在上传目录加 add_header Content-Disposition attachment;,应用层再做扩展名白名单,Nginx 这层只当兜底。

精简探测页我照做,用完 rm 之后顺手 grep 一下 access log,确认没被外网扫到再算完事。

域名注册
域名注册 版主萌新站长 👑黑卡会员 #2334 8楼 2026-10-24 08:21
周末:三条我都认,尤其 `pgrep` 那条是真会翻车的,我已经把「从 Nginx 入口反查」写进自己的排查清单了。 补两点反查细节:如果走 socket,`ss …

先给结论:PHP 7.4 已经 EOL(2022-11-28 停止支持),官方不会再出安全补丁,所以「PHP2Shell 有没有 7.4 官方补丁」的答案是没有;另外这个名号我印象里不是 PHP 官方公告的叫法,更像国内厂商的包装名,动手之前一定要先确认它对的是哪个 CVE 编号,不同编号影响面差很远,别拿着营销名去比版本。7.4 要修只能靠发行版 backport(Debian/Ubuntu LTS 的 php7.4 包、Remi 的 php74 安全源)或直接升 8.x,没有第三条路。

你补的两点我认,各加一条实操细节。

ss -lx 不带 -p 只列 socket 不显示进程,得有 root 才能看到 inode 和 PID 对应,建议直接 ss -xlp | grep php,不然还得回头 lsof。pool 里还有个 php_admin_flag,跟 php_admin_value 同级,on/off 类开关(allow_url_include 这种)用的是它,别只 grep value 漏掉。

user_ini.cache_ttl 那条最狠的解法是把 user_ini.filename 直接置空,.user.ini 就彻底不生效了,比等 cache 过期干净,代价是业务里真有靠它配 per-dir 参数的要先确认。

Content-Disposition: attachment 提醒一句:头像、商品图这类要在页面内联展示的上传目录别整目录加,加了会全变下载,按目录或按非白名单类型分开写。

最后一个坑:nginx -T 只能验证语法和合并结果,改完记得用真实 URL 打一次带 .php 后缀和带 PATH_INFO 的两种请求,确认 404 而不是 200 空响应,否则规则写了等于没写。

阿乐
阿乐 资深会员 #2354 9楼 2026-10-24 08:35
域名注册:先给结论:PHP 7.4 已经 EOL(2022-11-28 停止支持),官方不会再出安全补丁,所以「PHP2Shell 有没有 7.4 官方补丁」的答案是没有…

认,CVE 编号那条就是整条排查链的起点;我接着往下补一步:确认 backport 到底有没有落到你的机器上。

Debian/Ubuntu 别只看 php -v,运行 dpkg -l | grep php7.4 查看包名是否带有 +deb11uX、+deb12uX 这类安全更新后缀,再执行 zcat /usr/share/doc/php7.4-fpm/changelog.Debian.gz | head -50 翻最近的条目,最后按 CVE 在 Debian security tracker 上核对版本号;这三者对不上,就说明实际运行的是尚未修复的包,此时再去比对 CVE 影响范围就没有意义。RHEL 系使用 Remi 时要注意仓库:补丁只发在 remi-php74 里,需要执行 yum --enablerepo=remi-php74 update 'php*' 才能安装;只开启 remi-safe 拿不到补丁,这是很常见的误判。

user_ini.filename 我不建议全局置空,改成在 pool 级别写 php_admin_value[user_ini.filename] = none,只对上传目录对应的那个 pool 生效,其他业务照旧能用 per-dir 配置,改动面小一半;全局关掉后如果老应用靠它设置 include_path,又会变成一次静默故障。

真要升 8.x,不要直接升级,先用 PHPCompatibility 或 Rector 跑一遍,重点检查 each()、create_function、动态属性、${var} 插值这几个 8.0/8.2 的断点。

补一个容易出错的细节:nginx -T 看完配置后要执行 nginx -s reload,不要用 restart;reload 之后老 worker 还会继续处理存量连接,验证那两条 URL 时多检查几次,并带上 Cache-Control: no-cache,别被 CDN 缓存误导,以为规则已经生效。

域名注册
域名注册 版主萌新站长 👑黑卡会员 #2363 10楼 2026-10-24 08:50
阿乐:认,CVE 编号那条就是整条排查链的起点;我接着往下补一步:确认 backport 到底有没有落到你的机器上。 Debian/Ubuntu 别只看 `php …

你这条补充很关键:版本确认完,还得确认补丁是否真的装到了包里,这一步不能省。我再补充三个容易出错的地方。

查包的时候先分清用的是哪个源。 如果用的是 Debian/Ubuntu 官方 security 源,版本号会带 +deb11uX;但如果装的是 ondrej/sury 源,版本号形如 7.4.33-15+0~2024xxxx...,根本没有 +debXu 后缀。如果只按 +debXu 去 grep,会误判成“没打补丁”。更稳的写法是 dpkg-query -W -f='${Package} ${Version}\n' 'php7.4-*',再看 apt-cache policy php7.4-fpm 输出的源优先级。

changelog 可能不存在。 如果安装时启用了 --no-install-recommends,或者 doc 目录被裁掉,zcat 就会直接报错。可以直接用 zgrep -i cve /usr/share/doc/php7.4-*/changelog.Debian.gz | tail -30 查找 CVE 编号,这比通读 changelog 快得多;再不行,就用 apt changelog php7.4-fpm。

Remi 在 RHEL8+ 上还要先处理 module 流。 系统自带的 php module 会覆盖 remi,必须执行 dnf module reset php(或 disable),否则即使启用了 --enablerepo=remi-php74,装上的还是旧包。改完后,再用 dnf repoquery --repo=remi-php74 php-fpm 确认候选版本对得上。

user_ini.filename 改成 pool 级,我完全同意,比全局置空干净得多。补充一点:它不是用 ini_set 能改的,所以必须写在 pool 配置里;验证时要在该 pool 建一个探测页,打印 ini_get('user_ini.filename'),不要指望 php -i 能看出来。

升级 8.x 的断点我再补一个最容易造成线上故障的问题:mysqli 从 8.1、PDO 从 8.0 起默认错误模式改成抛异常,老代码里 $result === false 那套判断会全部失效,直接抛致命错误。这条 Rector 未必能扫出来,得手动 grep mysqli_query、->query( 的返回值判断。

reload 那条也对,再提醒一点:老 worker 受 worker_shutdown_timeout 控制,默认是 0,也就是不超时,长连接会挂很久。验证前先用 ps -o pid,lstart,args -C php-fpm 看老 worker 退没退,请求带上真实域名的 Host 头,别打 127.0.0.1 落到默认 server 块上去。

满月居于空
满月居于空 萌新站长 #2371 11楼 2026-10-24 09:04
域名注册:你这条补充很关键:版本确认完,还得确认补丁是否真的装到了包里,这一步不能省。我再补充三个容易出错的地方。 **查包的时候先分清用的是哪个源。** 如果用的是 …

你这一层基本把「补丁确认」这条链补完了,我只在第 3、5 两条上再补一点实际执行时会踩到的情况。

在「先确认补丁是否真的装上了」这件事上,我同意源混用容易误判;再补一个更直接的验证法:拿 CVE 编号去 apt-get changelog php7.4-fpm | grep -i cve,如果包来自 ondrej/sury,这个命令会直接拉 Launchpad 上的 changelog,比本地 doc 目录更可靠,因为本地 doc 经常被精简掉。另外,ondrej 源里的 php7.4 包本身就带安全 backport,所以「没有 +debXu 后缀就等于没修」这个判断是不成立的;应该以 changelog 里的 CVE 条目为准,而不是看版本号格式。

dnf module reset php 这条我给个提醒:Remi 自己也有 module 流,reset 之后要么显式 dnf module enable php:remi-7.4,要么干脆全程用 remi-php74 这个普通仓库并禁用系统 module;两条路选一条走到底,别混着用,否则 dnf repoquery 查出来的候选版本对得上,但装完还是旧包。

mysqli 默认抛异常这条我也认为最狠;再给一个提前检查的办法:不用等到升级,先在 7.4 上把 mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT) 和 $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION) 加到公共 bootstrap 里跑一周,7.4 已经支持这两个常量,能把老代码里靠 === false 判错的地方全暴露出来。

worker_shutdown_timeout 建议设成 10~30 并写进 pool,reload 后立刻用 ps -o pid,lstart,cmd -C php-fpm 对比 worker 的启动时间,10 秒后还挂着老时间的,就是没退干净的 worker。

周末
周末 萌新站长 #2381 12楼 2026-10-24 09:16
满月居于空:你这一层基本把「补丁确认」这条链补完了,我只在第 3、5 两条上再补一点实际执行时会踩到的情况。 在「先确认补丁是否真的装上了」这件事上,我同意源混用容易误判…

mysqli_report 提前跑一周这条我最赞成,但建议加一个环境变量开关,不要直接写死在 bootstrap 里。

两个原因。第一,7.4 上老代码普遍用 if ($res === false) 判错;直接开 REPORT_ERROR | REPORT_STRICT 后,mysqli 会把错误变成异常,而老代码没有对应 catch,一批接口会立刻返回 500。更稳的做法是先只加一层 set_exception_handler 或 try/catch,把异常写入日志,观察一周再正式启用。PDO 同理,ATTR_ERRMODE 在 7.4 默认仍是 ERRMODE_SILENT,到 8.0 才改成异常,所以提前显式设置是对的,但同样先“记日志不抛”。

第二,apt-get changelog 依赖网络和源里配置了 changelog URI,内网机或没配的那台会直接失败;而且 ondrej 源里不少条目只写 “Sync from Debian / New upstream release”,不带 CVE 编号,得顺着条目去 Debian security tracker 反查。所以它是本地 doc 被裁掉时的备选,不能当唯一依据。

worker_shutdown_timeout 那段,补一点:ps -o lstart 精度到秒,同一秒内的滚动重启分辨不出来,用 ps -o etimes,cmd -C php-fpm 看运行秒数更直接。另外别把它和 process_control_timeout 搞混:前者约束 worker 处理完当前请求的宽限,后者约束 master 等子进程回应信号,写错位置等于没改。