OpenAI 的 php2shell-checker 適合拿來做第一輪檢查,但不能單靠它證明伺服器安全。它會將 HTTP 標頭或 php -v 顯示的 PHP 版本,與內建的修補版本門檻進行比對。因此,隱藏版本標頭可能導致結果為 UNKNOWN;另一方面,Linux 發行版若以 backport 方式套用修補,也可能讓已修好的伺服器看起來仍然容易受攻擊。
先看懂四種結果
OpenAI 官方儲存庫會回報三種主要狀態。請把它們視為初步分流訊號,而不是完整的漏洞評估結果。
| 輸出結果 | 結束代碼 | 代表什麼 | 下一步 |
|---|---|---|---|
OK | 0 | 已辨識的分支版本達到或高於最低修正版 | 確認實際提供網頁流量的 PHP 程序使用的就是這個版本 |
VULNERABLE | 1 | 偵測到的版本低於最低修正版 | 更新提供服務的 runtime、重新啟動 workers,再重新執行兩種檢查 |
UNKNOWN | 3 | 檢查器無法確認可辨識的版本或回應 | 改在伺服器本機檢查;不要把它當成已完成修補 |
| 參數無效 | 2 | 指令本身不符合格式 | 修正指令後重新執行 |
OpenAI 提供了哪些工具
公開的 php2shell-checker 儲存庫包含 Node.js 指令稿、測試,以及兩個 Codex skills:phpcheck-url 用來檢查可連線的網站,phpcheck-server 則適用於你能登入的伺服器。它的工作是整理版本證據,不會利用漏洞攻擊目標,也不會掃描應用程式程式碼。
依照情境選對檢查方式
檢查公開 URL
先 clone 或下載儲存庫,再用 Node.js 指令稿檢查網站:
node phpcheck.js "https://example.com"
遠端檢查會先送出 HTTP HEAD 請求,只有伺服器回傳 405 或 501 時才改用 GET。它會讀取 X-Powered-By 和 Server,不會跟隨重新導向,也不會檢查回應本文。
CDN、反向代理、expose_php=off 或自訂伺服器設定,都可能在回應送到檢查器前移除 PHP 版本。若結果是 VULNERABLE,就需要進行修補;若只是沒有版本標頭,則應改取得伺服器本機證據。
檢查本機 PHP binary
在已安裝 PHP 的機器上執行:
node phpcheck.js --local
這會對目前 PATH 中可用的 binary 執行 php -v。但它無法證明網站使用的就是同一個 binary:同一台主機可能同時存在獨立的 CLI、PHP-FPM、CGI、Apache module、container,或從原始碼編譯的多套安裝。
完成本機檢查後,還要找出實際連接網頁伺服器的 runtime。若是 container 部署,請檢查正在執行的 container,不要只看 image tag。若是套件管理的服務,則應確認相關的 PHP-FPM pool 或 Apache module,並查看供應商的套件變更紀錄。
安裝並使用 Codex skills
官方 README 提供了一個 Codex 指令,可以一次安裝兩個 skills:
$skill-installer install both skills from https://github.com/openai/php2shell-checker
接著依照你的存取權限選擇合適的介面:
$phpcheck-url check https://example.com
$phpcheck-server check the PHP version serving my site
Codex 並非必要。對 CI 工作或資源精簡的主機來說,Node.js 指令仍是最直接的介面。若要自動化,請加上 --json;其中的 patch_status 欄位會回傳 ok、vulnerable 或 unknown,不必另外撰寫腳本解析終端機上的文字。
驗證下載的 checker
這個儲存庫使用 Node 內建的測試 runner:
node --test phpcheck.test.js
測試套件會透過本機 HTTP 伺服器測試 CLI,也會在本機情境使用假的 PHP executable。README 表示,它不需要實際安裝 PHP,也不需要連線到外部網站。
把 UNKNOWN 當成除錯結果
請把 UNKNOWN 視為證據不足。目標可能隱藏 PHP 版本、使用尚未辨識的分支、沒有正常回應,或執行較新的 prerelease;這些情況都不能證明修補已經存在。
真實使用者 @jvr0x 用這句話概括了限制:
「它其實只是拿版本跟 10 月 20 日的門檻比對,所以隱藏標頭最後會得到 UNKNOWN。」
遠端指令看到的是 HTTP 回應,而不是反向代理後方的程序。既然版本標頭被隱藏,就應該改做伺服器端檢查,而不是反覆測試 URL。
遇到 UNKNOWN 後,可以依序進行:
- 在提供網站服務的主機上執行
node phpcheck.js --local。 - 確認請求實際到達的是 PHP-FPM、CGI、Apache
mod_php,還是 container。 - 查看供應商套件的變更紀錄,確認是否有 backport 修補。
- 更新後重新啟動相關的 PHP-FPM 或 Apache workers。
- 透過實際提供服務的路徑驗證,不要只檢查 shell 預設的
phpbinary。
如果沒有 SSH,URL 模式可能就是唯一能做的第一輪檢查。若共用主機隱藏了版本標頭,就需要請託管供應商確認實際提供服務的 runtime、套件修訂版本,以及 workers 是否已重新啟動。
版本表不能單獨決定是否完成修補
這個 checker 將 PHP 8.5.12、8.4.27、8.3.36 和 8.2.35 視為各分支的最低上游修正版。官方測試也確認 8.4.26 會被判定為 VULNERABLE,8.4.27 則是 OK;PHP 官方將 8.5.12、8.4.27、8.3.36 和 8.2.35 列為安全性版本。
| 分支 | 最低上游修正版 | 上一個被判定為有漏洞的版本 |
|---|---|---|
| PHP 8.5 | 8.5.12 | 8.5.11 |
| PHP 8.4 | 8.4.27 | 8.4.26 |
| PHP 8.3 | 8.3.36 | 8.3.35 |
| PHP 8.2 | 8.2.35 | 8.2.34 |
| 部署情境 | 為什麼單純比對版本可能誤導 | 應採用的證據 |
|---|---|---|
隱藏 X-Powered-By 或 Server 標頭 | 遠端 checker 無法辨識 PHP | 本機 runtime 清單 |
| Debian 或 Ubuntu 安全性套件 | 供應商可能在較舊的上游版本字串下 backport 修補 | 供應商公告與套件變更紀錄 |
| 同一台主機上的 PHP-FPM 與 CLI | php -v 顯示的可能是 CLI,而不是 HTTP worker | 網頁伺服器與 FPM 設定 |
| 快取的 container 或固定的 digest | registry tag 可能已更新,但正在執行的 container 仍是舊版本 | 執行中 container 的 image ID 與版本 |
| Apache 或 FPM workers 未重新啟動 | 修正版檔案雖已寫入磁碟,舊 workers 仍可能持續運作 | 程序啟動時間與重新啟動後的檢查結果 |
| 不支援的分支或 prerelease | checker 可能無法辨識供應商特有的修補狀態 | PHP 或供應商的安全性公告 |
儲存庫的限制說明指出,這項工具假設列出的分支在較新的穩定版本中包含修補,且無法獨立驗證修補是否真的存在。發行版可以在不更改上游版本號的情況下套用安全性 backport,因此,與其只看顯示的版本號,供應商的變更紀錄通常更具說服力。
更新 WordPress、Drupal、Laravel 或其他 PHP 應用程式,不等於修補 PHP engine。請更新 runtime 套件或 image、重新啟動 workers,並確認真正接受網頁請求的程序。
常見問題
哪一種輸出結果可以關閉 production 修補工單?
沒有任何單一輸出可以直接關閉工單。即使結果是 OK,仍應一併確認 serving SAPI、供應商 backport 狀態、所有相關的 vhost 或 container,以及套用修補後 workers 確實已重新啟動。