PHP 官方在 2026 年 10 月 21 日發布安全公告,一次揭露兩個重大遠端程式碼執行(RCE)漏洞:CVE-2026-109618 與 CVE-2026-109619。兩者在 CVSS v4.0 都是 9.2,官方的建議只有一句:立刻更新。
這篇整理我們實際處理這兩個 CVE 時的判斷與做法:哪些版本受影響、該升到哪一版、發行版與主機面板怎麼修,以及 PHP 7.4、8.0、8.1 這類官方不再出版的版本,在升級之前能先做什麼。文中不含任何利用方式,也不討論攻擊手法。
兩個 CVE 是什麼
| 編號 | 官方公告 | 問題性質 |
|---|---|---|
| CVE-2026-109618 | GHSA-w8hw-w9fx-j79w | 記憶體耗盡(OOM)處理路徑造成狀態損毀,進而導致 RCE |
| CVE-2026-109619 | GHSA-6wmf-4rv5-p3rp | 請求之間殘留過期的全域狀態,進而導致 RCE |
兩者的官方修補說明是同一句:避免在 bailout 之後狀態被破壞(Avoid state corruption via bailout)。
重點在影響面。凡是用 PHP 處理 Web 請求的部署都在範圍內:PHP-FPM、mod_php、php-cgi、FrankenPHP、LiteSpeed 皆然。其中 mod_php 風險最高,因為公開資料指出問題可在應用程式程式碼執行之前、請求初始化階段就觸發,不需要網站本身有任何漏洞。只提供命令列使用(CLI)而不接 Web 請求的 PHP,實際曝險低很多,但仍建議一併更新。
影響版本與官方修正版本
官方修正版本只涵蓋仍在支援期的分支,更新日期為 2026 年 10 月 22 日:
| 分支 | 修正版本 |
|---|---|
| PHP 8.5 | 8.5.12 |
| PHP 8.4 | 8.4.27 |
| PHP 8.3 | 8.3.36 |
| PHP 8.2 | 8.2.35 |
| PHP 8.1 及更舊 | 沒有官方版本 |
8.1 的最後一版是 2025 年 12 月的 8.1.34,之後不會再出版。官方對 7.4、8.0、8.1 提供的是最小範圍的原始碼 backport,供自行編譯或由發行版、長期支援廠商吸收。換句話說,這些版本能不能被修好,取決於你用的套件來源有沒有跟進。
第一步:先盤點,不要只看 CLI 版本
這次最容易出錯的是「以為升級了」。一台主機上常有多組 PHP:
- 系統預設的
php指令是 CLI,版本號不代表 FPM 版本。 - 使用第三方套件庫(例如 Sury)的主機,可能同時裝了多個分支,每個分支各有自己的 FPM 與 pool。
- 主機面板(例如 DirectAdmin CustomBuild)自行編譯的 PHP,與系統套件是兩套東西,
apt upgrade不會動它。
我們的做法是逐台列出實際提供 Web 服務的 PHP 版本,再對照 pool 與站點。唯讀盤點可以回答三件事:每個站點實際跑哪個分支、該分支有沒有官方修正版、更新後要重啟哪些服務。
判斷「修好了沒有」不能只看版本字串。尤其是發行版套件,版本號常常只是小幅上升,要看 changelog 有沒有明確列出這兩個 CVE,或用安全追蹤頁確認修正版本。後面會舉一個實際遇到的例子。
我們採用的修法
能升級的:用套件或面板升到官方修正版
- Debian 系統套件:Debian 已發布修正,bookworm 的
php8.2為8.2.35-1~deb12u1,trixie 的php8.4為8.4.27-1~deb13u1。只升級已安裝的php*套件即可,不需要整機升級。 - Sury 套件庫的多版本 PHP:8.2 升到 8.2.35、8.3 升到 8.3.36、8.4 升到 8.4.27、8.5 升到 8.5.12。
- 面板自行編譯的 PHP:只重建 PHP,不重建 Web 伺服器。重建前先備份選項檔、版本紀錄與目前的 PHP 目錄,重建後確認模組清單與重建前一致。
更新套件時我們用的原則是:
- 只動 PHP 相關套件,並指定只升級不新裝(
--only-upgrade),設定檔衝突時保留既有設定。 - 升級前先存一份目前的 PHP 套件清單,作為回滾依據。
- 升級後重啟所有 FPM 實例,再重載 Web 伺服器。只升級套件而不重啟,舊的 worker 仍在記憶體裡跑舊程式碼,這點很容易漏。
- 逐站做煙霧測試,並與升級前的狀態對照。升級前就是 403、525 的站點,升級後仍是 403、525 才算沒有變動,不要把既有的狀態誤判成這次造成的問題。
一個容易誤判的例子:版本號上升不代表已修補
Sury 套件庫的 PHP 5.6 在這次更新後,changelog 明確寫了 backport 自 8.2.35 並列出兩個 CVE,這可以視為已修補。同一次更新裡,PHP 8.1 也從 8.1.34-8 升到 8.1.34-9,但 changelog 完全沒有提到這兩個 CVE。
所以 8.1 即使「已更新到最新套件」,仍然要當作未修補,必須走下一節的緩解。這種情況只能靠讀 changelog 或安全追蹤頁判斷,不能用版本號上下猜。
無法升級時:PHP 7.4、8.0、8.1 的緩解
官方在公告中給了暫時性的緩解設定,同時明確說明這些不是修補,不能讓安裝變得安全,只是降低風險。我們對仍在使用 PHP 7.4 與 8.1 的環境,是這樣落地的。
針對 CVE-2026-109619
停用 session 上傳進度功能:
| |
有兩個細節:
- 要設在
php.ini或 FPM pool 設定裡,用 pool 的php_admin_flag鎖定。官方特別指出用ini_set或.user.ini不算數,因為那些是應用程式或使用者可以自行改動的層級。 - 改完要重啟所有 worker 才會生效。
PHP 8.5 另有一項緩解是關閉 fatal_error_backtraces 並重啟。我們沒有 8.5 的 Web 服務,所以這項只列在文中供參考。
針對 CVE-2026-109618
這個漏洞與記憶體耗盡的路徑有關,緩解的方向是讓請求不容易走到 OOM:
- 限制
post_max_size:不需要上傳的站點用 8M,需要上傳的用 32M 左右,並讓upload_max_filesize小於post_max_size。 - 在 Web 伺服器或反向代理層另外限制請求大小與請求數量。
- 用 cgroup 限制行程的實體記憶體。官方特別註明不要用
RLIMIT_AS、RLIMIT_DATA這類位址空間限制來代替。 - 官方列出的另一個選項是用不可覆寫的方式把
memory_limit設為-1。這個做法有明顯的副作用(單一請求可以吃光主機記憶體),除非有 cgroup 這類外部上限,否則我們不建議單獨採用。 - 把
memory_limit以php_admin_value固定,避免被.user.ini或ini_set改掉。
設定放哪裡才有效
我們把緩解寫在兩個層級,互相補強:
- 全域 ini:放進該版本 PHP 的額外設定目錄,新增一個獨立檔案(例如以 CVE 編號命名),內容只有上述三到四個設定。獨立檔案的好處是回滾只要刪檔,不會污染原有的
php.ini。 - FPM pool:在每個 pool 以
php_admin_flag/php_admin_value寫入session.upload_progress.enabled、post_max_size、upload_max_filesize,並把該 pool 目前實際生效的memory_limit原樣寫成php_admin_value,不要因為加了鎖定而意外改變行為。
改之前務必備份原設定檔,並用 php-fpm -t 之類的語法檢查通過再重啟。
上傳大小的代價要先講清楚
緩解會有實際的功能影響。我們有一個站點原本把上傳上限放寬到數百 MB,套用後變成約 30 MB,超過的上傳會失敗。這類站點需要事先通知,或改用分段上傳、直傳物件儲存等方式。與其事後被客訴,不如在套用前就把影響範圍告訴站點負責人。
面板會改寫你的設定
使用面板管理 PHP 時要特別留意:面板在重寫設定或重建 PHP 時,可能重新產生 pool 設定與 php-fpm.conf,你手動加進去的鎖定值有被覆蓋的風險。獨立的 ini 檔通常能保留,但如果沒有在 pool 層級用 php_admin_* 鎖定,仍可能被 .user.ini 覆蓋。我們的做法是把「面板重寫設定後要重新檢查緩解值」記在維運待辦裡,每次面板操作後重新驗證一次。
驗證:看實際生效值,不要看設定檔
改設定最常見的失誤是「檔案寫了,但實際沒生效」。驗證時要回答的是每個 pool 實際跑的值:
session.upload_progress.enabled是否為關閉post_max_size、upload_max_filesize、memory_limit是否為預期值- 實際執行的 PHP 版本是否為預期版本
要注意,在命令列執行 php -i 看到的是 CLI 的設定,不等於 FPM pool 的設定。我們是對每個 pool 的 FastCGI socket 做一次唯讀的設定查詢,逐 pool 對照上面幾個值。
另外做了兩件輔助確認:
- 官方修正版的二進位在版本資訊之外,也能用它是否包含修正後新增的行為特徵來交叉確認;面板編譯的 PHP 也確認有套用對應的 CVE 修補檔。
- 站點層面逐一確認回應碼,與升級前對照。
我們沒有對任何正式站執行公開流傳的利用程式。這類程式可能觸發記憶體耗盡甚至實際取得執行權限,不適合拿來驗證正在營運的系統。等效的做法是檢查版本、修補特徵與生效設定,這樣已經足以判斷是否曝險,又不會留下風險。
建議的處理順序
給需要自己跑一輪的人,大致可以這樣排:
- 盤點所有對外提供 PHP 的主機,包含系統套件、第三方套件庫、面板編譯三種來源。
- 能升級的先升級到 8.2.35、8.3.36、8.4.27、8.5.12 或發行版對應的修正版,並重啟所有 FPM。
- 無法升級的(7.4、8.0、8.1)先確認套件來源有沒有 backport;沒有的話套用上述緩解,同時規劃遷移到仍在支援的分支。
- 逐 pool 驗證實際生效值,逐站做回應碼檢查並與升級前對照。
- 把面板重寫設定、之後新增的 pool 這些會讓緩解失效的情況列入後續檢查。
- 在 Web 伺服器、反向代理或 CDN 層,限制請求大小與速率,作為多一層保護。
結論
這次的修補本身不複雜,難的是「哪些 PHP 真的被修好了」。同一台機器上的 CLI、多個 FPM 分支、面板編譯版本、第三方套件庫,每一種來源的更新路徑與判斷方式都不同;版本號上升也不保證已經包含修補。
對無法升級的舊版本,上面的設定只能降低風險,不能取代升級。這類站點最後還是要排進遷移計畫,改到仍受官方支援的分支,避免下次又要再面對一次「沒有官方版本可用」。
