PHP-FPM 設定緩解 · 2026-10-21

CVE 緩解 2026-10-21

CVE-2026-109618CVE-2026-109619

關閉 session 上傳進度、啟用 session 嚴格模式、隱藏 PHP 版本標頭,並調整記憶體和上傳大小上限。確認伺服器上的 PHP 版本後在版本列選擇,之後的路徑和指令會一起換成該版本。

1

確認版本

確認主機上的 PHP 版本,再從下方的版本列選擇對應的版本。

shell
sudo php -v

php -v 顯示的是 CLI 的版本。主機裝了多個版本時,可以用 ls /etc/php/ 列出全部,以實際跑網站的 FPM 版本為準。

PHP 版本
PHP 8.4 · 官方支援中
2

查看目前的 session 設定

修改前先看一次目前的值,之後可以和第 5 步的結果比對。

shell
sudo php-fpm8.4 -i | grep session

重點看 session.upload_progress.enabled 和 session.use_strict_mode 這兩行。

3

更新 php ini

新增一個緩解設定檔。conf.d 裡的檔案會被 FPM 自動載入,檔名用 99- 開頭讓它最後載入,覆蓋前面的設定。

shell
sudo vi /etc/php/8.4/fpm/conf.d/99-fix-php2shell.ini
99-fix-php2shell.ini
; Fix CVE-2026-109618 / 109619 (2026-10-21)
session.upload_progress.enabled = 0
session.use_strict_mode = 1
expose_php = Off
4

更新 php-fpm pool 設定

在 pool 設定裡用 php_admin_flag 和 php_admin_value 鎖住這些設定,網站程式就無法再自行改回來。

shell
sudo vi /etc/php/8.4/fpm/pool.d/www.conf
www.conf · 加在 [www] 區段內(檔案末尾即可)
; Fix CVE-2026-109618 / 109619 (2026-10-21)
php_admin_flag[session.upload_progress.enabled] = off
php_admin_flag[session.use_strict_mode] = on
M

upload_max_filesize 自動算成 30M(post_max_size × 0.95,無條件捨去)

www.conf · 接在上一段後面
; Fix CVE-2026-109618 / 109619 (2) (2026-10-22)
php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 32M
php_admin_value[upload_max_filesize] = 30M
5

檢查並套用

先用 -t 檢查設定語法,通過後再 reload。reload 會平滑重載 worker;設定沒有生效時再改用 restart。

shell
  • 檢查設定語法
    sudo php-fpm8.4 -t
  • 查看 session 相關設定
    sudo php-fpm8.4 -i | grep session
  • 檢查通過後 reload
    sudo php-fpm8.4 -t && sudo systemctl reload php8.4-fpm
  • 檢查通過後 restart(reload 沒生效時用)
    sudo php-fpm8.4 -t && sudo systemctl restart php8.4-fpm
  • 查看服務狀態
    sudo systemctl status php8.4-fpm
6

檢查 PHP-FPM

不經過網站,直接用 cgi-fcgi 連到 FPM 的 socket 執行一支暫存的 PHP 檔,列出這個 pool 實際生效的設定,包括 pool 裡的 php_admin_flag 和 php_admin_value。跑完會刪掉暫存檔並移除 libfcgi-bin。

shell · 整段貼進終端機執行
function check_fpm_via_cgi {
sudo apt install -y libfcgi-bin   # 沒裝的話

sudo tee /tmp/test-php-2f67c46f.php >/dev/null <<'EOF'
<?php
$os = @parse_ini_file('/etc/os-release')['PRETTY_NAME'] ?? '?';
echo "OS        : $os\n";
echo "PHP       : ", PHP_VERSION, " (", PHP_SAPI, ")\n";
echo "php.ini   : ", php_ini_loaded_file(), "\n";
echo "user      : ", function_exists('posix_geteuid') ? posix_getpwuid(posix_geteuid())['name'] : get_current_user(), "\n\n";
foreach ([
  'memory_limit','post_max_size','upload_max_filesize','session.upload_progress.enabled',
  'session.use_strict_mode','expose_php',
  'max_input_vars','max_input_nesting_level','max_file_uploads','max_execution_time',
  'display_errors','display_startup_errors','log_errors','error_reporting',
  'open_basedir','disable_functions','allow_url_fopen','allow_url_include','opcache.enable',
] as $k) {
  $v = ini_get($k);
  printf("%-34s %s\n", $k, $v === false ? '(n/a)' : ($v === '' ? '(empty/off)' : $v));
}
EOF
sudo env SCRIPT_FILENAME=/tmp/test-php-2f67c46f.php REQUEST_METHOD=GET cgi-fcgi -bind -connect /run/php/php8.4-fpm.sock
sudo rm /tmp/test-php-2f67c46f.php
sudo apt purge -y libfcgi-bin
}

check_fpm_via_cgi

history -c

socket 路徑用的是 Debian/Ubuntu 預設的 /run/php/php8.4-fpm.sock,pool 的 listen 改過的話要換成實際路徑。主機原本就裝了 libfcgi-bin 的話,記得拿掉 apt purge 那一行。最後的 history -c 會清掉目前 shell 的指令紀錄。

重點看前六項:memory_limit 是 -1,post_max_size 是 32M,upload_max_filesize 是 30M,session.use_strict_mode 是 1,session.upload_progress.enabled 和 expose_php 是 (empty/off) 或 0。

7

重新啟動 PHP-FPM

收尾時重新啟動一次 PHP-FPM。指令會先用 -t 檢查設定,通過才會 restart。

shell
sudo php-fpm8.4 -t && sudo systemctl restart php8.4-fpm
8

觀察 FPM 記憶體用量

memory_limit 設成 -1 之後,單一請求不再有記憶體上限。重新啟動後可以用這行看每個 FPM 程序佔用的記憶體,依 RSS 由大到小排序,只列前幾筆。

shell
ps -o pid,rss,cmd -C php-fpm8.4 --sort=-rss | head
範例輸出 · PHP 7.4
  PID   RSS CMD
27951 30904 php-fpm: master process (/etc/php/7.4/fpm/php-fpm.conf)
27974 20260 php-fpm: pool www
27987 14852 php-fpm: pool www
27988 14852 php-fpm: pool www
27989 14852 php-fpm: pool www
27973 14848 php-fpm: pool www
27975 14848 php-fpm: pool www
27976 14848 php-fpm: pool www
27977 14848 php-fpm: pool www

RSS 的單位是 KB。範例裡 master 約 30 MB,每個 www worker 約 15–20 MB。某個 worker 明顯比其他大很多時,可以從它的 PID 追查是哪個請求吃掉記憶體。

9

升級到修補版

設定緩解只是暫時擋住,升級 PHP 才是正式修補。修補版由 ondrej/php PPA 提供,只有 Ubuntu 22.04 和 24.04 拿得到;18.04 和 20.04 的 PPA 已經停止更新,只能繼續靠上面的設定緩解。

PHP 8.4 的修補版是 8.4.27-1 以後的版本,完整版本號後面還會接 +ubuntu24.04.1+deb.sury.org+1 這類字尾。

升級前
  • 更新套件清單
    sudo apt update
  • 確認 Candidate 是修補版
    apt-cache policy php8.4-fpm
  • 記下目前載入的模組
    php8.4 -m > ~/php-mods-before.txt
  • 備份設定目錄
    sudo tar czf ~/php8.4-etc-$(date +%F).tgz /etc/php/8.4
  • 模擬升級,不會真的安裝
    sudo apt install --only-upgrade -s 'php8.4-*'

Candidate 還不是修補版,代表 PPA 還沒上架,這時升級拿不到這次的修補。模擬結果裡一大串 Skipping ..., it is not installed 可以忽略,要看的是最後會升級哪些套件,以及是不是 0 newly installed, 0 to remove。

升級 · 改過的設定檔一律保留
sudo apt -o Dpkg::Options::="--force-confold" install --only-upgrade 'php8.4-*'

同一個小版本裡升級,模組不用重裝,conf.d/99-fix-php2shell.ini 也不會被動到。--force-confold 會讓改過的 www.conf 保留你的版本;沒加這個參數時,如果被問到要不要換,選 N。

升級後
  • 確認套件版本
    dpkg-query -W -f='${Version}\n' php8.4-fpm
  • 比對升級前後的模組
    php8.4 -m | diff ~/php-mods-before.txt - && echo "模組都一樣"
  • 找出新版範本檔(有的話代表新套件改過範本,你的設定仍保留)
    sudo find /etc/php/8.4 -name '*.dpkg-*' -o -name '*.ucf-*'
  • 確認 pool 設定還在
    grep -n 'php_admin' /etc/php/8.4/fpm/pool.d/www.conf
  • 檢查設定語法
    sudo php-fpm8.4 -t

7.2 到 8.1 的修補是回移植進舊版本,php -v 顯示的版本號不會變,要用上面的 dpkg-query 看套件版本。最後再跑一次第 6 步,確認 FPM 實際用到的值。