ISPConfig 多 PHP 版本環境下應對 CVE-2026-115560 漏洞:全域修復與服務重啟實戰

在現今複雜的網頁寄存環境中,主機控制面板通常需要同時支援多個不同的 PHP 版本(從舊版的 PHP 5.4、5.6 到現代的 PHP 7.x、8.x),以滿足不同客戶或網站系統的相容性需求。然而,當突發重大安全漏洞(如針對 session.upload_progress 的 CVE-2026-115560 遠端程式碼執行漏洞)降臨孤立或多版本並存的伺服器時,管理員如何在不破壞多 PHP 架構的前提下進行一次性全域快速止血,就成為了一門重要的技術課題。

本文將以實際在 ISPConfig 伺服器上的防護經驗為例,分享如何利用 Shell Script 精準修復多版本 php.ini 並安全重啟服務。

一、 挑戰所在:ISPConfig 多 PHP 架構的配置特點

以採用 Remi 套件庫並透過 ISPConfig 管理多 PHP 版本的 Linux 伺服器為例:

  • 路徑分散:系統中往往同時存在多個獨立的 PHP 運行環境,其設定檔並非集中在單一的 /etc/php/ 目錄,而是散落于 /opt/remi/、/etc/opt/remi/ 或 /etc/ 等自訂路徑中。
  • 版本眾多:從 PHP 5.4 一直到 PHP 8.3 共存,若手動逐一打開每個版本的 php.ini 進行修改,不僅耗時費力,還極容易遺漏高風險版本。

二、 實戰演練:一次性全域修改 php.ini

為了精準切斷 CVE-2026-115560 的漏洞觸發路徑(即關閉 session.upload_progress.enabled 且不影響正常的檔案上傳功能),我們不需要「一刀切」禁止所有上載,而是可以直接在伺服器端執行一段迴圈腳本,自動掃描並更新所有版本的 php.ini。

1. 執行自動掃描與批次修改指令

透過以下 Shell 命令,系統會自動在 /opt/、/etc/ 及 /usr/local/ 中尋找所有的 php.ini 檔案:

  • 若檔案中已存在該參數,則自動將其設定為 0;
  • 若不存在,則在檔案結尾自動追加 session.upload_progress.enabled = 0。

Bash

for ini in $(find /opt/ /etc/ /usr/local/ -type f -name "php.ini" 2>/dev/null); do
    if grep -q "session.upload_progress.enabled" "$ini"; then
        sed -i 's/^[# \t]*session\.upload_progress\.enabled\s*=.*/session.upload_progress.enabled = 0/' "$ini"
    else
        echo "session.upload_progress.enabled = 0" >> "$ini"
    fi
    echo "已更新: $ini"
done

執行後,終端機會清晰列出所有被成功加固的 php.ini 路徑,確保無一遺漏。

三、 批次重啟 PHP-FPM 與網頁伺服器

設定檔修改完成後,必須重啟對應的 PHP-FPM 服務與網頁伺服器(以 Apache / httpd 為例),才能使全域修改正式生效。

1. 迴圈重啟各版本 PHP-FPM 服務

Bash

for s in php54-php-fpm php56-php-fpm php70-php-fpm php72-php-fpm php73-php-fpm php74-php-fpm php81-php-fpm php83-php-fpm; do
    systemctl restart $s 2>/dev/null
    echo "已重啟: $s"
done

2. 重啟網頁伺服器

Bash

systemctl restart httpd

四、 總結與防護建議

透過這套自動化處理方案,系統管理員能在短短數分鐘內完成整台 ISPConfig 多 PHP 版本伺服器的漏洞防護加固:

  1. 精準 surgical 級別防禦:關閉 session.upload_progress.enabled 有效阻斷了 RCE 攻擊途徑。
  2. 業務零中斷:完整保留了網站正常的檔案上傳與互動功能,避免粗暴禁止上傳所帶來的業務癱瘓。
  3. 高效維運:利用 Shell 迴圈完美解決了多版本、分散式路徑的管理難題。

建議各位主機維護團隊在面對新型自動化攻擊浪潮時,優先採用這類結構化、自動化的指令碼來提升應變效率,保障伺服器的絕對安全。

Rocky Linux 安裝多版本 PHP 及切換 CLI PHP 教學

本文示範如何在 Rocky Linux 9 / 10 上:

  • ✅ 安裝多個 PHP 版本(7.4 / 8.1 / 8.2)
  • ✅ 同時並存
  • ✅ 切換 CLI PHP 預設版本
  • ✅ 啟用不同版本的 PHP-FPM

一、安裝 Remi Repository

Rocky Linux 預設只提供單一 PHP 版本,因此需要使用 Remi repo 來安裝多版本 PHP。

1️⃣ 安裝 Remi 與 EPEL

dnf install -y https://rpms.remirepo.net/enterprise/remi-release-$(rpm -E %rhel).rpm
dnf install -y epel-release
dnf update -y

2️⃣ 重設系統 PHP module

dnf module reset php -y

⚠️ 注意:不要使用 dnf module enable php:remi-xx
否則會變成單一 system PHP。


二、安裝多個 PHP 版本(並存模式)

我們使用 Remi 的 平行安裝版本(remi-safe)。


✅ 安裝 PHP 7.4

dnf install -y php74 php74-php-cli php74-php-fpm php74-php-mysqlnd php74-php-gd php74-php-mbstring php74-php-xml php74-php-curl php74-php-zip

安裝完成後可用:

/usr/bin/php74

✅ 安裝 PHP 8.1

dnf install -y php81 php81-php-cli php81-php-fpm php81-php-mysqlnd php81-php-gd php81-php-mbstring php81-php-xml php81-php-curl php81-php-zip

✅ 安裝 PHP 8.2

dnf install -y php82 php82-php-cli php82-php-fpm php82-php-mysqlnd php82-php-gd php82-php-mbstring php82-php-xml php82-php-curl php82-php-zip

三、查看已安裝 PHP 版本

ls /usr/bin/php*

或:

alternatives --list | grep php

四、切換 CLI PHP 版本(推薦方法)

建議使用 alternatives 系統機制 切換 CLI PHP。


1️⃣ 註冊不同版本到 alternatives

如果尚未註冊:

alternatives --install /usr/bin/php php /usr/bin/php74 74
alternatives --install /usr/bin/php php /usr/bin/php81 81
alternatives --install /usr/bin/php php /usr/bin/php82 82

2️⃣ 選擇預設 CLI PHP

alternatives --config php

系統會顯示:

1   /usr/bin/php74
2   /usr/bin/php81
3   /usr/bin/php82

輸入對應號碼即可切換。


3️⃣ 確認切換成功

php -v

應顯示所選版本。


五、臨時使用指定版本(不切換系統)

如果只想執行某個版本,不需更改預設:

php74 -v
php81 -v
php82 -v

或執行腳本:

php81 script.php

六、啟用不同版本 PHP-FPM

多版本 FPM 可以同時存在。

例如啟動 PHP 7.4 FPM:

systemctl enable php74-php-fpm
systemctl start php74-php-fpm

查看狀態:

systemctl status php74-php-fpm

同樣方法可啟動 8.1 或 8.2。


七、檢查目前 CLI PHP 位置

which php
php -v

應顯示:

/usr/bin/php

八、總結

✅ 使用 Remi repo 安裝多版本 PHP
✅ 使用 remi-safe 平行安裝
✅ 使用 alternatives 切換 CLI
✅ FPM 可同時並存
✅ 不建議使用 module enable(除非只需要單一版本)

RESTful API 規格參考指南

什麼是 RESTful API?

REST(Representational State Transfer)是一種軟體架構風格,用於設計網路服務。RESTful API 遵循 REST 原則,提供一致且直觀的方式來操作資源。

核心原則核心原則

統一介面(Uniform Interface)
所有資源都通過統一的介面進行操作,使用標準的 HTTP 方法。

無狀態(Stateless)
每個請求都包含處理該請求所需的所有資訊,伺服器不保存客戶端狀態。

可快取(Cacheable)
回應應該明確標示是否可以快取,以提高效能。

分層系統(Layered System)
客戶端無法判斷是直接連接到終端伺服器,還是連接到中間層。

HTTP 方法對應

HTTP 方法用途範例 URL說明
GET讀取資源GET /users獲取所有用戶
GET讀取單一資源GET /users/123獲取 ID 為 123 的用戶
POST創建資源POST /users創建新用戶
PUT完整更新資源PUT /users/123完整更新用戶 123
PATCH部分更新資源PATCH /users/123部分更新用戶 123
DELETE刪除資源DELETE /users/123刪除用戶 123

URL 設計規範

使用名詞,不用動詞

✅ 正確:GET /users
❌ 錯誤:GET /getUsers

使用複數形式

✅ 正確:/users, /products, /orders
❌ 錯誤:/user, /product, /order

階層關係表示

GET /users/123/orders # 獲取用戶 123 的所有訂單
GET /users/123/orders/456 # 獲取用戶 123 的訂單 456

查詢參數用於篩選和分頁

GET /users?page=2&limit=10&status=active
GET /products?category=electronics&sort=price&order=desc

HTTP 狀態碼

成功回應 (2xx)

  • 200 OK – 請求成功
  • 201 Created – 資源創建成功
  • 204 No Content – 請求成功但無回應內容(通常用於 DELETE)

客戶端錯誤 (4xx)

  • 400 Bad Request – 請求語法錯誤
  • 401 Unauthorized – 需要驗證
  • 403 Forbidden – 禁止訪問
  • 404 Not Found – 資源不存在
  • 405 Method Not Allowed – HTTP 方法不被允許
  • 422 Unprocessable Entity – 請求格式正確但語義錯誤

伺服器錯誤 (5xx)

  • 500 Internal Server Error – 伺服器內部錯誤
  • 502 Bad Gateway – 網關錯誤
  • 503 Service Unavailable – 服務暫時無法使用

回應格式規範

成功回應範例

{
    "success": true,
    "data": {
        "id": 123,
        "name": "John Doe",
        "email": "john@example.com"
    },
    "message": "User retrieved successfully"
}

錯誤回應範例

{
    "success": false,
    "error": {
        "code": "VALIDATION_ERROR",
        "message": "Email is required",
        "details": {
            "field": "email",
            "value": null
        }
    }
}

列表回應範例

{
    "success": true,
    "data": [
        {"id": 1, "name": "User 1"},
        {"id": 2, "name": "User 2"}
    ],
    "pagination": {
        "current_page": 1,
        "per_page": 10,
        "total": 100,
        "total_pages": 10
    }
}

版本控制

URL 路徑版本控制

GET /v1/users
GET /v2/users

Header 版本控制

GET /users
Accept: application/vnd.api+json;version=1

認證與授權

Bearer Token

Authorization: Bearer your_access_token_here

API Key

X-API-Key: your_api_key_here

最佳實踐

使用 HTTPS
所有 API 端點都應該使用 HTTPS 來確保資料傳輸安全。

提供清楚的錯誤訊息
錯誤回應應該包含足夠的資訊讓開發者理解問題所在。

實作速率限制

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1640995200

使用適當的 Content-Type

Content-Type: application/json
Content-Type: application/xml

支援 CORS

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization

常見 API 端點範例

# 用戶管理
GET /users # 獲取用戶列表
POST /users # 創建新用戶
GET /users/{id} # 獲取特定用戶
PUT /users/{id} # 更新用戶
DELETE /users/{id} # 刪除用戶

# 文章管理
GET /posts # 獲取文章列表
POST /posts # 創建新文章
GET /posts/{id} # 獲取特定文章
PUT /posts/{id} # 更新文章
DELETE /posts/{id} # 刪除文章

# 巢狀資源
GET /users/{id}/posts # 獲取用戶的文章
POST /users/{id}/posts # 為用戶創建文章
GET /posts/{id}/comments # 獲取文章的評論

PHP_EOL is newline character in a cross-platform-compatible

PHP_EOL = cross-platform-compatible newline character

PHP_EOL is ostensibly used to find the newline character in a cross-platform-compatible way, so it handles DOS/Unix issues.

Note that PHP_EOL represents the endline character for the current system. For instance, it will not find a Windows endline when executed on a unix-like system.

Example (Save the $DataArray to $filename):

file_put_contents($filename, implode(PHP_EOL, $DataArray), LOCK_EX);
Reference:
Adam Bellaire (2008), https://stackoverflow.com/questions/128560/when-do-i-use-the-php-constant-php-eol

Remove empty array elements

// PHP 7.4 and later
print_r(array_filter($linksArray, fn($value) => !is_null($value) && $value !== ''));

// PHP 5.3 and later
print_r(array_filter($linksArray, function($value) { return !is_null($value) && $value !== ''; }));

// PHP < 5.3
print_r(array_filter($linksArray, create_function('$value', 'return $value !== "";')));
Reference:

BoltClock (2020), https://stackoverflow.com/questions/3654295/remove-empty-array-elements

update/upgrade PHP 7.2 to latest version

source : https://askubuntu.com/questions/1146109/how-to-update-upgrade-php-7-2-to-latest-version-safely

sudo add-apt-repository ppa:ondrej/php
sudo apt update
sudo apt install php7.3 # The latest version in this repo for 18.04 is currently php7.4.  
apt-cache search php7.3 | grep php7.3  
apt install $(apt list --installed | grep php7.2- | cut -d'/' -f1 | sed -e 's/7.2/7.3/g') 

自建了 php autoload ,並同時使用 composer 的 autoload,應該注意什麼?

不知道大家有沒有看過下面的 php autoload 教學?

Eric G. Huang 不像樣工程師
PHP系列 – Autoload 自動載入
http://justericgg.logdown.com/posts/196891-php-series-autoload

跟據這個教學,我解決了很多 class include require 檔案引入的困難,而且用了很長時間。

下面提供一些參考資料,讓大家可以分享。

Composer Basic usage: autoloading
https://getcomposer.org/doc/01-basic-usage.md#autoloading

PHP PSR-4 Autoloader 機制
http://blog.tonycube.com/2016/09/php-psr-4-autoloader.html

什麼是 PHP Standards Recommendations(PSR)(PHP標準建議)?
https://www.php-fig.org/psr/

PHP PSR-4 Autoloader 機制
http://blog.tonycube.com/2016/09/php-psr-4-autoloader.html

Composer Basic usage: autoloading
https://getcomposer.org/doc/01-basic-usage.md#autoloading

運用Composer Autoloader

1. 準備自建的 namespace 和 目錄位置

e.g. 
namespace1 = application\control\, 目錄位置 = {user_directory}/application/control 
namespace2 = application\module\, 目錄位置 = {user_directory}/application/module 
namespace3 = application\mouule\sql\, 目錄位置 = {user_directory}/application/module/sql
Class 例子:

<?php
//application/module/config.php

namespace application\module;

class config
{


    function __construct() {
        ...
    }
}

2. 在 composer.json 加入自建的 namespace 和 目錄位置

{
    "autoload": {
        "psr-4": {
            "application\\control\\": "application/control/",
            "application\\module\\": "application/module/",
            "application\\module\\sql\\": "application/module/sql/"
        }
    }
}

3. 進入 console mode,使用用戶權限及用戶的root目錄,執行下面的程式

composer dump-autoload

4. 在需要的php 程式中,頂部加入(因為我的 composer 是放在 /vendor中,請自行改進)

require __DIR__ . '/vendor/autoload.php';