🚨你的圖片壓縮外掛正在幫駭客拆福袋?ShortPixel 高危漏洞全解析(CVE-2026-17086/CVSS 8.8)
🖼️ 圖片壓縮小幫手的「拆信」壞習慣,怎麼變成駭客的秘密信箱?ShortPixel 高危漏洞全解析(CVE-2026-17086/CVSS 8.8)
🔥 站長懶人包:
全球裝機量約 30 萬 站的圖片壓縮神器 ShortPixel Image Optimizer,被抓到一個「作者以上權限就能塞包裹,等你家剛好有另一個粗心的鄰居才會出事」的 PHP 物件注入漏洞(CVE-2026-17086,CVSS 8.8 高風險)。它自己單獨存在其實沒有殺傷力,真正麻煩的是——你網站上裝的那十幾二十個外掛和主題,隨便一個裡面藏著能被串聯利用的「小零件」,這個漏洞就有機會被放大成刪檔案、偷資料,甚至整站淪陷。目前還沒看到有人真的拿它下手,但漏洞已經公開,建議還在用 6.5.5 以下 版本的網站,儘快升級到 6.5.6 以上 🔒
內容目錄
🧐 第一章:專門幫照片「瘦身」的外掛,怎麼會變成駭客的信箱?
大家想像一下這個畫面:如果你去超商寄包裹,店員不只收件,還把你包裹裡夾帶的「會自己動的神秘指令紙條」拿出來,完全沒檢查是誰寫的,就直接照著指令把店裡的收銀機打開——你會不會覺得這家店的安管超母湯?這次的 CVE-2026-17086 漏洞,玩的正是這套可怕的心理戰 🪤
你的 WordPress 網站就像一間相片館,ShortPixel Image Optimizer 是館裡專門幫客人把大張照片壓縮成輕巧版本的沖印機器人。平常運作很正常:客人(也就是網站的作者、編輯)把圖片交出去,機器人壓縮完再還回來。但這台機器人有個壞習慣——當它在處理文章裡面那種「像俄羅斯娃娃一樣一層包一層」的複雜資料(技術術語叫:巢狀 JSON)時,會不小心把裡面藏著的「自動組裝說明書」(這就是傳說中的 PHP 物件注入)給直接拆開來執行,完全沒有確認這份說明書是不是館方自己寫的 📦
其實,這份壞壞的說明書如果自己躺在那裡,什麼事都不會發生。但可怕的是,如果這間相館(你的網站)剛好還放著其他有缺陷的工具,這份說明書一發動,就會像推倒骨牌一樣,把別的工具也推倒!資安圈把這種「借用系統裡本來就存在的合法零件來搞破壞」的手法叫做 POP 鏈(Property-Oriented Programming Chain)。第一片骨牌倒下,一路推推推,最後駭客就能執行系統指令或把你辛苦寫的檔案刪光光啦 🀄
📢 碎碎念:這台沖印機器人,其實已經因為同一個壞習慣被抓包兩次了
翻一下 ShortPixel 的歷史紀錄會發現一件有趣的事——這不是它第一次因為「反序列化沒做好」出包。早在 2023 年 9 月,5.4.2 版就修過一次編輯者權限就能觸發的 PHP 物件注入;2026 年 4 月 的 6.4.4 版,又補了一次類似的物件注入問題(CVE-2026-39471)。同一種壞習慣在三年內反覆發作,某種程度上也說明了「處理使用者輸入的序列化資料」這件事,真的是一個很容易一犯再犯的技術債 🪤
⏱️ 從「悄悄補好」到「正式公開」,時間軸其實比你想的更有意思
這次的通報流程走得算是相當扎實,值得把時間軸攤開來看一次:
| 日期 | 事件節點 | 發生了什麼事 |
|---|---|---|
| 2026-07-24 | CVE 編號預留 | 由 Wordfence 擔任 CNA(CVE 編號指派機構),先保留 CVE-2026-17086 這個編號 |
| 2026-07-31 | 通報廠商 | 獨立資安研究員 Josh Bolding 透過 Wordfence 的漏洞獎金計畫,正式通知 ShortPixel 開發團隊 |
| 2026-09-09 | 官方悄悄補好門 | ShortPixel 在 6.5.6 版的更新日誌中寫著「強化了內容替換機制,處理序列化資料時會擋掉 PHP 物件的實例化」——這時漏洞細節還沒有公開 |
| 2026-09-17 | 細節正式揭露 | Wordfence Threat Intelligence 公開完整的漏洞說明 |
| 2026-09-18 | CVE 正式發布 | NVD、CVE.org 同步收錄,CVSS 3.1 評為 8.8(高風險) |
你有沒有發現一件蠻有意思的事?補丁其實比公開細節早了整整八天上線。這其實是負責任揭露(Responsible Disclosure)該有的樣子——研究員先私下通知廠商、廠商先悄悄把門鎖修好、確定大家都升級有機會之後,才把「這道門原來有問題」的細節公開,避免還沒補完就先教壞人怎麼開鎖 🔐
截至報告產出時,✅ 已確認的事實是:CISA 的「已知遭利用漏洞」(KEV)清單裡還沒看到這一支,資安社群目前也沒觀察到大規模自動化攻擊。不過 🟡 合理推論是,細節既然已經公開,接下來這段「大家還沒升級完」的空窗期,正是漏洞掃描腳本最愛出手的時間,所以真的不建議拖 🕳️
⚠️ 講白了,這串漏洞真正能對你的網站幹嘛?
跟大部分「有密碼保護就安全」的直覺不太一樣,這次的威脅模型需要兩個條件同時湊在一起才會真正爆炸。我們照事實確定的程度,一層一層拆給你看:
- 🏮 ✅ 已確認事實:攻擊者必須先登入,而且至少要有「作者」等級的權限。匿名訪客、剛註冊還沒審核的訂閱者,都碰不到這個漏洞——它需要一個能發文的帳號。
- 🏮 ✅ 已確認事實:ShortPixel 自己身上沒有可以直接搞破壞的零件(POP 鏈)。如果你的網站環境非常乾淨(只裝了 WordPress 核心跟修補後的 ShortPixel),單靠這支外掛沒辦法造成實質傷害。
- 🏮 🟡 合理推論:多數 WordPress 網站其實都裝了 10 到 30 個外掛或主題。裝得越多,無意間存在可利用零件的機率也越高——只要環境裡剛好有那個零件,具備作者權限的帳號就有機會把兩者串起來。
- 🏮 🔴 假設情境:最糟的狀況下,持有作者權限的帳號(或是被盜用的員工帳號)可能刪除伺服器上的敏感檔案、偷走資料庫裡的個資,甚至在系統裡種下後門,讓整個網站被接管。
📢 碎碎念:作者權限聽起來不高,但其實比你想的更「有料」
根據 Wordfence 發布的《2024 年度 WordPress 資安報告》,2024 年公開揭露的所有外掛漏洞中,需要「投稿者(Contributor)」以上權限才能觸發的類型占了 34%,是所有權限層級裡占比最高的一群。換句話說,「需要作者以上權限」聽起來像是一道防線,但在真實世界的資料裡,這其實是攻擊者最常見的起跑線之一,特別是那些開放網友投稿,或是買東西就送作者身分的網站,這其實是駭客最愛瞄準的起跑線喔 📊
🎭 第二章:先別急著慌,看看你比較接近哪一種情境
好啦,看到這裡先喝口茶壓壓驚!🍵 不同的身分有不同的應對方式,趕快看看你比較符合哪一種情境 👇
🙋 我只是負責發文的小編,網站後台我不太熟
如果你的日常就是寫寫字、傳傳美圖,完全不懂那些寫著火星文的程式碼,那你只需要做一件事:去後台確認外掛版本,然後勇敢地按下「更新」!如果發現不認識的帳號,請趕快呼叫工程師。接下來深奧的技術章節你可以直接跳過,直接看第三章的「五分鐘無痛自救指南」就好囉 💪!
🩵 小資族荷包試算: 放心,ShortPixel 的安全更新是完全免費的,這是官方佛心推送的修補,不用擔心被強迫升級付費版才能拿到喔!
👨💻 我自己架站,手上還管著好幾個客戶的 WordPress
如果你手上有好幾個客戶的網站要顧,手動一個一個點真的會點到懷疑人生。站長建議你直接滑到後面的「技術深潛篇」,那邊有幫你準備好的 WP-CLI 批次指令,貼上去就能光速盤點,超爽的 👇
🏢 我的網站牽涉到會員資料,或是有多位作者共同投稿
如果你的網站是熱鬧的論壇或電商,只要會員升級就能拿到「作者」權限,那事情就大條了!你不只要更新外掛,還得當一次「風紀股長」,把所有掛著作者權限的帳號全盤掃描一次。那些離職員工、幽靈寫手,通通給他們降級或刪除,這比事後補破網便宜太多啦 🩺
🧑🔧 DevOps 小知識:為什麼「只更新外掛」有時候還不夠?
更新外掛就像是把被撬壞的門鎖換新,絕對能擋住「下一次」的攻擊。但如果駭客已經趁你還沒更新時溜進來,在你家客廳藏了備用鑰匙(後門檔案),那你就算換了十道鎖,他還是能在你家來去自如!所以,如果漏洞已經公開好一陣子,排查潛在的後門跟更新一樣重要喔 🛡️
🚦 恐怖情境模擬:那個被遺忘在角落的「殭屍外掛」
很多老闆為了讓網站跑快點,三年前裝了 ShortPixel 之後,就再也沒有點開過它。大家心想:「反正照片壓縮都有在跑,沒壞幹嘛修?」
錯了 🙅 這就像家裡三年沒開過的後門,你以為沒事,其實鎖早就生鏽了。只要這支外掛還處於「啟用」狀態,它就是駭客眼中的大肥肉。如果有天某個離職員工的舊帳密被盜,這支一直「很正常」的外掛,馬上就會變成引爆整個網站的第一塊骨牌 🆘
🚥 劃重點:「一直沒出過問題」不等於「沒有風險」,只要外掛還在啟用清單裡,攻擊面就一直存在——這也是這種需要湊條件才會爆炸的漏洞最容易被忽略的地方 🙅♂️
🛟 第三章:五分鐘無痛自救指南,電腦小白也能跟著做
不管你屬於上面哪一種情境,這一段的步驟建議每個人都先跑一遍——不用懂程式,跟著畫面點就好 🤏
- ㊀ 先去哪裡看版本?
登入你的 WordPress 後台,眼球往左邊看,找到「外掛」👉「已安裝的外掛」。在長長的清單裡,找出名字叫 ShortPixel Image Optimizer 的傢伙。它的名字下面就會顯示現在是幾版喔 🔢 - ㊁ 看到版本號之後,怎麼判斷自己安不安全?
睜大眼睛看!如果版本號是 6.5.5 或更小(像是 6.5.0、6.4.4 之類的),代表你現在正身處險境!如果已經是 6.5.6 或更大,恭喜你,可以先去泡杯咖啡了 ㊙️ - ㊂ 需不需要馬上更新?
當然要!而且是立刻、馬上!直接在那個畫面點擊「立即更新」按鈕,等圈圈轉完,重整網頁看到版本變成 6.5.6 以上就大功告成了 💪 - ㊃ 更新會不會讓網站版面跑掉、甚至當機?
安啦!ShortPixel 這次修的都是機器人內部的「消化系統」,並沒有去動對外的圖片壓縮功能,所以版面跑掉的機率微乎其微。但身為一個專業的站長,更新前先「備份」一下網站,絕對是積陰德的好習慣 🛟 - ㊄ 盤點一下:網站上有哪些人擁有「作者」以上的權限?
到後台左邊找「使用者」👉「所有使用者」,看看名單裡有沒有你不認識的張三李四。如果有離職的小編、或是很久沒聯絡的外部寫手,別猶豫,直接考慮把他們降級成「訂閱者」或停用帳號。因為這個漏洞就是靠「作者」權限才能發動的喔 🔍 - ㊅ 不敢自己動手、或是完全看不懂這些選單怎麼辦?
這超級正常,別害羞!你可以把這篇文章的網址直接貼給幫你做網站的工程師,或是主機商的客服說:「救命啊!我的外掛有『CVE-2026-17086』漏洞,請幫我更新檢查!」台灣的主機商通常都很熱心會幫忙的 🤗
🫂 新手求助:完全沒有測試環境,也不敢亂點怎麼辦?
如果你真的不敢直接按更新,至少先到後台的「工具」👉「匯出」,做一次內容備份。接著打電話或發工單給你的主機商客服,請他們幫你確認每天的「自動備份」是不是完整的。等確認備份沒問題,再進行更新,這樣就算出錯,風險也會小很多啦 🤗
⚠️ 安全提醒:在正式環境升級外掛前,建議先於測試環境備份並驗證,避免網站崩潰。
🤿 第四章:技術深潛篇——這串漏洞的程式碼底層到底哪裡出包
接下來這段是給想知道「為什麼會這樣」的人看的——如果你只想知道怎麼補洞,前面那段其實已經夠用了。但如果你也好奇這種等級的漏洞底層長什麼樣子,我們繼續往下拆 ⚔️
| 評估指標 | 具體資訊 |
|---|---|
| 外掛名稱 | ShortPixel Image Optimizer – Optimize Images, Convert WebP & AVIF |
| CVE 編號 | CVE-2026-17086 |
| CVSS 3.1 評分 | 8.8(High) |
| CVSS 向量 | CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H |
| 受影響版本 | ≦ 6.5.5 |
| 安全版本 | ≧ 6.5.6(2026-09-09 釋出) |
| 漏洞類型 | PHP 物件注入(PHP Object Injection)/CWE-502 不可信資料反序列化 |
| 通報者 | 獨立資安研究員 Josh Bolding(透過 Wordfence 漏洞獎金計畫) |
🧑🔧 DevOps 小知識:CVSS 向量裡的 PR:L 有點容易讓人誤判
NVD 把這條漏洞的「所需權限」標示為 PR:L(低權限),意思是攻擊者需要一定程度的登入權限(作者以上)。但要注意的是,這不代表威脅等級真的很低——作者權限在多作者部落格、開放投稿平台上其實相當容易取得,而且一旦環境中存在可搭配的 POP 鏈,後續的破壞力(機密性、完整性、可用性三項都被標示為 High)跟未授權攻擊沒有太大差別。所以從防禦者的角度來看,還是應該把邊界防護當成「隨時可能被觸發」在防,不能因為向量上寫著 PR:L 就掉以輕心 🙅
🟡 合理推論(根據多份公開分析比對 WordPress Trac 上的原始碼路徑推得,官方尚未公開完整修補 diff):問題出在外掛處理文章內容裡「巢狀 JSON」資料的邏輯上。ShortPixel 有一套叫做 Replacer 的模組,負責在圖片的 AI SEO 資訊產生流程中,把文章內容裡的特定欄位抓出來做替換處理;當這段流程碰到已經被序列化過的資料時,若沒有針對「這串字串到底能不能被還原成物件」做嚴格的類別白名單檢查,就有可能讓攻擊者放進去的字串被直接還原成 PHP 物件,而不是單純被當成一般文字內容處理。
🧊 冷知識:為什麼特別強調是「巢狀」JSON?
開發者為了相容舊資料格式,常常會把一個 JSON 字串再包進另一個 JSON 欄位裡,就像俄羅斯娃娃一層套一層。如果程式只檢查外層那一層 JSON 格式對不對,內層藏著的序列化字串反而就這樣被放過了——這也是為什麼這類漏洞常常不是「完全沒做檢查」,而是「檢查得不夠深」😮💨
🎯 骨牌是怎麼一片片倒下去的:概念層級的攻擊路徑
整條攻擊鏈揉合了「免驗證不成立、但驗證門檻不高」跟「條件式串聯」兩個特性,拆開來看邏輯其實相當工整:
- 🏮 第一步:取得作者以上的帳號。攻擊者需要先登入,可能透過社交工程、密碼填充攻擊(拿其他外洩帳密去試),或是網站本身開放註冊且未嚴格審核。
- 🏮 第二步:送出帶有惡意序列化資料的文章內容。攻擊者透過文章編輯介面,或是 WordPress 的 REST API 端點(例如 /wp-json/wp/v2/posts),提交一篇內容裡藏著特製巢狀 JSON 結構的文章。
- 🏮 第三步:外掛在背景處理時觸發反序列化。ShortPixel 在解析這篇文章內容的過程中,把攻擊者構造好的字串當成合法資料還原成 PHP 物件,在記憶體裡創建出這個物件。
- 🏮 第四步:如果環境裡剛好有可用的 POP 鏈,骨牌開始倒下。物件被銷毀或被呼叫的瞬間(技術上叫觸發 __destruct 或 __wakeup 等魔術方法),如果系統裡另一個外掛或主題剛好有能被串接的程式片段,就會一路推導出任意檔案刪除,甚至遠端程式碼執行。
✅ 已確認的影響:攻擊者能在滿足權限條件時,讓外掛把他控制的字串還原成 PHP 物件,觸發相關的魔術方法。🟡 合理推論的影響:若環境中存在可用的 POP 鏈,可能被串聯成任意檔案刪除、讀取敏感資料(資料庫憑證、API 金鑰)。🔴 假設情境的影響:在最嚴重的狀況下,攻擊者可能完全接管網站,並以伺服器為跳板對內部網路進行橫向移動。✅ 已確認的緩解因素:ShortPixel 本身不含可用的 POP 鏈,且目前尚無公開的武器化攻擊工具或在野利用證據 🛑
🕵️♀️ 第五章:資安排查 Checklist——DevOps 該怎麼確認自己有沒有已經中招
🤖 重要提醒:以下指令與排查流程由 DeepSeek、Gemini、Meta AI 產生、Claude 複查修正
雖然已盡力對照常見實務經驗與 WP-CLI 官方文件,統一改寫成路徑雙引號包覆、`find -print0` 安全迴圈等寫法,但仍可能因伺服器環境、權限設定或外掛版本差異而產生疏漏。正式環境(尤其是正在營業中的網站)的高風險操作——包含更新、停用、刪除檔案、變更權限或執行批次腳本——請務必由熟悉該站架構的專業人員進行最後確認與執行,切勿直接複製貼上後就按下 Enter 🧙
如果你是自己架 VPS、同時管理多個 WordPress,這一節最重要的不是「背一條神奇迴圈指令」,而是先搞清楚自己到底是哪一種 WordPress 架構。搞錯架構,後面的批次指令就算語法完全正確,也可能處理錯對象;這一節會依序走過環境判斷 👉 盤點(版本比對)→ Log 日誌排查 👉 後門檢查點 👉 加固檢查五個關卡,並且分別涵蓋單一網站、獨立多站、Multisite 三種環境 📜
🧭 ㊀ 環境判斷:你在管一個 WordPress,還是一整台 WordPress 伺服器?
決策原理:單一站、獨立多站、Multisite 三者的 WP-CLI 操作對象與資料範圍完全不同。判斷錯誤會導致後續盤點、備份、更新全部跑偏——例如對 Multisite 網路裡的某個子站單獨更新,卻沒發現外掛檔案其實是全網共用的,白做工還可能造成版本不一致。
WP_PATH="/var/www/example.com/public_html"
# ❶ 檢查是否為 Multisite 網路環境(不產生輸出,靠結束碼判斷)
if wp --path="$WP_PATH" core is-installed --network 2>/dev/null; then
echo "這是 Multisite 網路"
else
echo "這不是 Multisite 網路"
fi
# ❷ 檢查 wp-config.php 中是否定義了 MULTISITE 常數
grep -n "MULTISITE" "$WP_PATH/wp-config.php"
# ❸ 若不是 Multisite,尋找系統中所有的 wp-config.php,確認是單站還是獨立多站
sudo find /var/www -type f -name "wp-config.php" -print0 2>/dev/null |
while IFS= read -r -d '' config; do
printf '找到網站設定檔:%s\n' "$config"
done
預期輸出範例:
# ❶ 若為 Multisite 這是 Multisite 網路 # ❷ 的輸出 12:define( 'MULTISITE', true ); 13:define( 'SUBDOMAIN_INSTALL', false ); # ❸ 若為獨立多站,會列出多個路徑 找到網站設定檔:/var/www/site1.com/public_html/wp-config.php 找到網站設定檔:/var/www/site2.com/public_html/wp-config.php
分支判斷邏輯:
- ⭕ ❶ 顯示「這是 Multisite 網路」→ 進入 ✴️ Multisite 流程,記得外掛檔案是全網共用,通常只需在網路層級操作一次。
- ⭕ ❶ 顯示「這不是 Multisite 網路」,且 ❸ 只找到一個 wp-config.php → 💠 單一網站。
- ⭕ ❸ 找到多個 wp-config.php,且分別位於不同目錄、彼此無關 → ❇️ 獨立多站,後續每一站要各自處理,不能混在一起跑。
🔎 ㊁ 盤點(版本比對):先回答「哪些站裝了它、目前是哪一版?」
決策原理:不建議在指令裡把安全版本寫死成 6.5.6,而是讓 WP-CLI 動態抓取 WordPress.org 官方認定的目前版本與可更新版本,確保無論什麼時候執行都能拿到正確資訊 💪
# 💠 單一網站
WP_PATH="/var/www/example.com/public_html"
wp --path="$WP_PATH" plugin get shortpixel-image-optimiser --fields=name,status,version,update,update_version
# ❇️ 獨立多站:逐站盤點,動態抓取檔案擁有者,避免權限錯亂
SEARCH_ROOT="/var/www"
find "$SEARCH_ROOT" -type f -name "wp-config.php" -print0 |
while IFS= read -r -d '' config; do
site_path="$(dirname -- "$config")"
owner="$(stat -c '%U' -- "$config" 2>/dev/null || echo 'www-data')"
printf '=== 網站:%s(擁有者:%s) ===\n' "$site_path" "$owner"
sudo -u "$owner" -- wp --path="$site_path" plugin get shortpixel-image-optimiser \
--field=version 2>/dev/null || printf '(外掛未安裝)\n'
done
# ✴️ Multisite:外掛檔案全網共用,查一次主站即可代表整個網路
WP_PATH="/var/www/network.example.com/public_html"
wp --path="$WP_PATH" plugin get shortpixel-image-optimiser --fields=name,status,version,update,update_version
預期輸出範例:
name: shortpixel-image-optimiser status: active version: 6.5.4 update: available update_version: 6.5.6
分支判斷邏輯:
- 🔴 版本為 6.5.5 或更舊 → 視為受影響,進入第六章的備份/應急/修補流程。
- 🟢 版本已是 6.5.6 或更新 → 版本條件已解除,但如果這個站曾經對外公開超過一段時間,仍建議繼續往下做 Log 排查與後門檢查,確認舊版暴露期間沒有被人摸過。
- 🟠 顯示「(外掛未安裝)」→ 該網站不受此漏洞影響,無需進一步處理。
📊 ㊂ Log 日誌排查特徵:不是看到怪字元就緊張,而是找「路徑+特徵+來源」的組合
決策原理:此漏洞觸發於已登入使用者的文章儲存動作,官方目前未公開確切的觸發端點,因此以下屬於 🟡 合理推論的通用排查特徵,並非官方證實的攻擊指紋——真正有價值的不是找某個「神奇 IP」,而是確認是否曾出現對應路徑、序列化資料特徵與異常來源的組合 🔎
ACCESS_LOG="/var/log/nginx/access.log"
ERROR_LOG="/var/log/nginx/error.log"
# 若使用 Apache,請改成:
# ACCESS_LOG="/var/log/apache2/access.log"
# ERROR_LOG="/var/log/apache2/error.log"
# ❶ 搜尋針對文章 REST API 或 admin-ajax.php 的 POST 請求,且帶有序列化物件特徵(O:數字:)
if [ -f "$ACCESS_LOG" ]; then
sudo grep -aE "POST.*(wp-json/wp/v2/posts|admin-ajax\.php)" "$ACCESS_LOG" |
grep -aE "(O%3A[0-9]+|O:[0-9]+:)" | tail -n 50
else
printf 'LOG NOT FOUND: %s\n' "$ACCESS_LOG"
fi
# ❷ 搜尋錯誤日誌中與 unserialize 相關的錯誤訊息
if [ -f "$ERROR_LOG" ]; then
sudo grep -ai "unserialize" "$ERROR_LOG" | tail -n 30
fi
預期輸出範例:
# 若未遭觸發:無任何符合輸出 # 若發現異常: 192.168.1.100 - - [18/Sep/2026:10:15:30 +0800] "POST /wp-json/wp/v2/posts/123 HTTP/1.1" 200 4521 "..." "O%3A8%3A%22Example%22..."
分支判斷邏輯:
- 🟢 只有正常的登入者發文紀錄,沒有序列化特徵 → 目前沒有直接證據顯示已遭觸發,但仍建議繼續往下做後門檢查。
- 🟡 出現序列化特徵,但來源 IP 是已知的合法作者 → 有可能是誤判(例如某些自訂欄位外掛本身就會存序列化資料),需人工確認上下文,不要直接當成攻擊。
- 🔴 出現序列化特徵,且來源 IP 或帳號不是平常的發文者 → 進入第六章「A. 已中招/疑似中招」流程。
- 🟡 日誌已被輪替或清空 → 檢查 logrotate 設定,確認是否還有備份日誌可查。
🕳️ ㊃ 後門檢查點:檔案完整性不能只看外表
決策原理:PHP 物件注入若真的串上了 POP 鏈,攻擊者常見的落腳點是 mu-plugins 目錄(因為這裡的檔案不需要在後台被「啟用」就會自動載入)、近期被修改過的 PHP 檔案,以及資料庫裡異常的管理員帳號或選項值 🕵️♀️
WP_PATH="/var/www/example.com/public_html"
MU_PLUGINS="$WP_PATH/wp-content/mu-plugins"
# ❶ 檢查 mu-plugins 目錄
if [ -d "$MU_PLUGINS" ]; then
find "$MU_PLUGINS" -maxdepth 2 -type f -iname "*.php" -print
else
printf 'MU-PLUGINS DIRECTORY NOT FOUND: %s\n' "$MU_PLUGINS"
fi
# ❷ 檢查近 14 天內被修改過的 PHP 檔案(排除快取目錄避免雜訊)
find "$WP_PATH" -type f -name "*.php" -mtime -14 -not -path "*/wp-content/cache/*" -print
# ❸ 檢查是否有異常的管理員或作者帳號
wp --path="$WP_PATH" user list --role=administrator --fields=ID,user_login,user_email,user_registered
wp --path="$WP_PATH" user list --role=author --fields=ID,user_login,user_email,user_registered
# ❹ 檢查 wp_options 中跟 shortpixel 相關的異常記錄(用 heredoc 避免多層跳脫符號互相干擾)
QUERY_FILE="$(mktemp)"
cat > "$QUERY_FILE" <<'EOF'
SELECT option_name, LEFT(option_value, 200) AS value_preview, autoload
FROM wp_options
WHERE option_name LIKE '%shortpixel%'
OR option_name LIKE '%sp_%'
ORDER BY option_id DESC
LIMIT 20;
EOF
wp --path="$WP_PATH" db query < "$QUERY_FILE"
rm -f -- "$QUERY_FILE"
預期輸出範例:
# ❶ 正常情況(無異常檔案) MU-PLUGINS DIRECTORY NOT FOUND: /var/www/example.com/public_html/wp-content/mu-plugins # ❸ 的輸出 +----+------------+-------------------+---------------------+ | ID | user_login | user_email | user_registered | +----+------------+-------------------+---------------------+ | 1 | admin | admin@example.com | 2024-01-15 08:00:00 | +----+------------+-------------------+---------------------+
分支判斷邏輯:
- 🔴 mu-plugins 目錄裡出現不是你們部署流程建立的 PHP 檔案,特別是檔名看起來像亂數字串 → 高度可疑,先隔離不要直接刪除,並進入第六章「A. 已中招」流程。
- 🟡 發現近 14 天內被修改的 PHP 檔案,且修改時間跟異常登入或發文時間吻合 → 需進一步檢查檔案內容。
- 🟡 wp_options 裡出現含有 base64_encode 或 eval 字串的可疑選項值 → 高度可疑,需人工鑑識。
- 🟢 上述四項都沒有發現異常 → 這一輪檢查沒有找到明確跡象,但任何不認識的管理員帳號仍值得再確認一次——「陌生」不等於「惡意」,可能是維運商的既有帳號,先問過再判斷。
🔧 ㊄ 加固檢查:就算修好了,也把第二道門一起鎖起來
決策原理:更新外掛是根本,但這次的漏洞類型(不可信反序列化)本身提醒我們,就算這次補好了,同一類問題也可能在別的外掛裡重演——所以除了升級之外,還應該從 PHP 執行環境跟檔案權限兩個角度收斂攻擊面,降低萬一再出現類似漏洞時的「爆炸半徑」 🔍
# ❶ 備份設定檔(用時間戳記避免覆蓋掉之前的備份) PHP_INI="/etc/php/8.4/fpm/php.ini" sudo cp "$PHP_INI" "$PHP_INI.BAK.$(date +%F_%H%M%S)" # ❷ 停用高風險函式(冪等替換:重複執行也不會出問題) sudo sed -i -E 's/^[;#]*\s*disable_functions\s*=.*/disable_functions = exec,passthru,shell_exec,system,proc_open,popen/' "$PHP_INI" # ❸ 重啟 PHP-FPM 讓設定生效 sudo systemctl restart php8.4-fpm # ❹ 確認設定已生效 php -i | grep disable_functions # ❺ 檢查並收緊 wp-config.php 的檔案權限 WP_PATH="/var/www/example.com/public_html" sudo chmod 640 "$WP_PATH/wp-config.php" stat -c "%a %n" "$WP_PATH/wp-config.php"
預期輸出範例:
# ❹ 的輸出 disable_functions => exec,passthru,shell_exec,system,proc_open,popen => exec,passthru,shell_exec,system,proc_open,popen # ❺ 的輸出 640 /var/www/example.com/public_html/wp-config.php
分支判斷邏輯:
- 🟢 disable_functions 已包含必要的高風險函式 → 不需重複設定,繼續下一步即可。
- 🟠 wp-config.php 的權限大於 644 → 建議收緊到 600 或 640。
- 🟡 若 open_basedir 尚未設定 → 建議設定為 WordPress 根目錄與必要的暫存目錄,限制 PHP 存取檔案系統的範圍,這樣即使日後真的有檔案操作類的漏洞,破壞範圍也會被限制住。
🧊 冷知識:WordPress 的「鹽值」到底是什麼碗糕?
你可能沒注意過,但每個 wp-config.php 裡都藏著八組隨機字串,正式名稱叫「金鑰與鹽值(Keys and Salts)」,負責加密使用者的登入憑證。只要重新產生這八組字串,所有目前已登入的使用者(包括潛在的攻擊者)都會被強制登出。這個功能在懷疑網站已經被摸過的時候特別好用,就像發現有人偷了鑰匙,最快的處理方式不是換密碼,而是直接把整棟樓的鎖都換掉 🔑
📋 ㊅ 三環境速查表:把五個關卡濃縮成一張表
| 階段 | 💠 單一網站 | ❇️ 獨立多站 | ✴️ Multisite |
|---|---|---|---|
| ① 環境判斷 | wp core is-installed –network 判斷結束碼 | find /var/www -name wp-config.php 逐一列出 | 同左,結束碼為 0 即為 Multisite |
| ② 盤點版本 | wp plugin get shortpixel-image-optimiser | 逐站以 stat -c ‘%U’ 取得擁有者後執行 | 查主站一次即代表全網 |
| ③ Log 排查 | 單一 access.log 掃描 | 逐 VirtualHost 掃描 | 共用 Log,依 URL/時間回推子站 |
| ④ 後門檢查 | mu-plugins+近期檔案+帳號 | 逐站各自檢查,不互相取代 | mu-plugins 全網共用,帳號分 Network/Site 兩層看 |
| ⑤ 加固檢查 | php.ini+檔案權限 | 逐站檢查,不假設一站正常全部正常 | php.ini 屬伺服器層級,通常一次到位 |
🤔 讀到這裡有點喘?幾個常見焦慮先幫你解掉
- 🏮 Q:更新之後,網站的圖片壓縮功能會不會壞掉?
A:ShortPixel 這次修的是內部的內容替換邏輯,並沒有變更對外的壓縮 API 呼叫方式,所以功能異常的機率不高。更新完成後,建議隨手在媒體庫上傳一張測試圖片,確認它還能正常觸發背景壓縮就可以放心了 📸 - 🏮 Q:廠商說「我們又沒開放註冊,這功能不會有事」,該信嗎?
A:先別急著全信。這個漏洞真正需要的只是「有一個作者以上權限的帳號」,不一定要靠開放註冊——舊員工帳號沒收回、弱密碼被撞庫成功,都是常見的取得管道。除了確認註冊功能有沒有開放,同步盤點一次目前的作者名單,會比只問「有沒有開放註冊」更保險 🔎 - 🏮 Q:我完全看不懂技術深潛篇那些指令,是不是就沒救了?
A:完全不會。前面「五分鐘無痛自救指南」那一段從頭到尾不需要碰任何指令,只要在後台點幾下滑鼠就能完成最基本的防護,技術深潛篇是給有 SSH 權限、想深入排查的人看的補充內容 🤗
🛠️ 第六章:修不了就先擋——短期應急與正式修補三部曲
前面解決的是「怎麼判斷」,這一章則進入真正的維運 Runbook。無論你屬於哪一種情境,強制流程都是同一套:盤點 👉 備份 👉 Dry-run 👉 更新 👉 驗證,每個階段都必須完成,才能進入下一階段 🧹
🚨 ㊀ A. 已中招/疑似中招:先止血再修補
決策原理:一旦第五章的排查出現明確跡象(mu-plugins 可疑檔案、日誌裡出現序列化特徵且來源不明、資料庫留言或選項值異常),優先目標是「切斷觸發鏈路+保留證據+隔離後門」,而不是急著更新——更新只是後續步驟。最大的敵人不是漏洞本身,而是在沒有留下證據的情況下直接清理,反而把最有價值的線索一起刪掉了 🧯
WP_PATH="/var/www/example.com/public_html"
STAMP="$(date +%Y%m%d-%H%M%S)"
# ❶ 立即隔離:停用 ShortPixel,切斷觸發點
wp --path="$WP_PATH" plugin deactivate shortpixel-image-optimiser
# ❷ 緊急取證:打包日誌與可疑目錄,先保留現場再動手清理
FORENSIC_DIR="$HOME/wp-incident-forensics-${STAMP}"
mkdir -p "$FORENSIC_DIR"
sudo cp -a "$WP_PATH/wp-content/mu-plugins" "$FORENSIC_DIR/" 2>/dev/null
sudo tar -czf "$FORENSIC_DIR/access_logs_${STAMP}.tar.gz" \
/var/log/nginx/*access*.log 2>/dev/null
wp --path="$WP_PATH" db export "$FORENSIC_DIR/db_snapshot_${STAMP}.sql"
printf '取證資料已存放於:%s\n' "$FORENSIC_DIR"
# ❸ 隔離可疑檔案(先移到隔離區,不要直接刪除,保留鑑識線索)
QUARANTINE_DIR="$HOME/wp-quarantine-${STAMP}"
mkdir -p "$QUARANTINE_DIR"
find "$WP_PATH/wp-content/mu-plugins" -type f -iname "*.php" -mtime -30 -print0 2>/dev/null |
while IFS= read -r -d '' suspect; do
printf '隔離可疑檔案:%s\n' "$suspect"
chmod 000 -- "$suspect"
mv -- "$suspect" "$QUARANTINE_DIR/"
done
# ❹ 強制刷新 Salt 值,讓所有現存的登入 Session(包括攻擊者的)立刻失效
sudo cp "$WP_PATH/wp-config.php" "$WP_PATH/wp-config.php.BAK.${STAMP}"
wp --path="$WP_PATH" config shuffle-salts
# ❺ 重設所有管理員的密碼
wp --path="$WP_PATH" user list --role=administrator --field=ID |
while IFS= read -r uid; do
wp --path="$WP_PATH" user update "$uid" --user_pass="$(openssl rand -base64 24)"
printf '已重設使用者 ID %s 的密碼\n' "$uid"
done
# ❻ 一併清除所有現存的登入 Session(跟刷新 Salt 雙重保險)
wp --path="$WP_PATH" eval 'wp_destroy_all_sessions(); echo "所有 Session 已清除\n";'
預期輸出範例:
# ❶ 的輸出 Plugin 'shortpixel-image-optimiser' deactivated. Success: Deactivated 1 of 1 plugins. # ❹ 的輸出(官方標準訊息) Success: Shuffled the salt keys. # ❺ 的輸出 已重設使用者 ID 1 的密碼 # ❻ 的輸出 所有 Session 已清除
分支判斷邏輯:
- ⭕ 取證與隔離步驟全部成功 → 進入下方「C. 正式修補」流程;如果是企業、電商或會員制網站,同步通知內部資安或維運負責人,把 $FORENSIC_DIR 保留在受保護的位置。
- 🟠 隔離過程發現明確的後門檔案內容(例如包含 eval、base64_decode 的可疑程式碼) → 建議把取證資料提供給資安事件應變團隊做深入分析,並持續監控後續一週的流量與帳號活動。
- 🔴 網站牽涉會員個資或線上收款,且確認曾遭觸發 → 這已經不只是「更新外掛」的層級,建議同步啟動內部資安事件應變流程,留存稽核紀錄,以應付後續可能的通報責任。
🧯 ㊁ B. 短期應急:還沒確認中招,但現在還不能立刻更新
🚨 短期應急 ≠ 正式修補
停用外掛、限制帳號權限都屬於緩衝措施,目標是縮短攻擊面暴露的時間,真正的修補仍然是升級到 6.5.6 或更新版本 🧱
WP_PATH="/var/www/example.com/public_html" # 選項一:如果暫時不需要用到圖片壓縮功能,直接停用是最乾淨的做法 wp --path="$WP_PATH" plugin deactivate shortpixel-image-optimiser # 選項二:如果業務依賴圖片壓縮不能停用,先收斂作者權限的暴露面 # 列出目前所有作者帳號,供人工逐一確認是否仍需保留這個權限層級 wp --path="$WP_PATH" user list --role=author --fields=ID,user_login,user_email # 選項三:關閉非必要的 Pingback/Trackback,降低其他被動探測管道 wp --path="$WP_PATH" option update default_ping_status closed wp --path="$WP_PATH" option update default_pingback_flag 0
預期輸出範例:
# 選項一的輸出 Success: Deactivated 1 of 1 plugins. # 選項二的輸出 +----+------------+-------------------+ | ID | user_login | user_email | +----+------------+-------------------+ | 3 | author01 | a01@example.com | +----+------------+-------------------+
分支判斷邏輯:
- 🟢 可以安全停用外掛 → 保留停用狀態,盡快排入下方「C. 正式修補」的維護窗口。
- 🟠 網站核心流程依賴自動圖片壓縮(例如電商站每天大量上架新品) → 不建議直接停用,先透過收斂作者帳號+加固 php.ini 降低風險,並儘速安排正式修補窗口。
㊂ C. 正式修補:盤點 👉 備份 👉 Dry-run 👉 更新 👉 驗證
🩵 荷包試算:這一整套流程要花多少錢?
從盤點到驗證,全程只要有 SSH 權限就能自己動手,本質上是免費的維運工時。真正會花到錢的地方,通常是「已經中招之後」——請資安顧問做鑑識、清除後門的工時,或是網站被列入安全黑名單之後重建流量的成本。這筆帳怎麼算,都是現在花半小時走完這套流程比較划算 💸
❶ 備份:遵循「3-2-1 備份原則」
決策原理:最佳實踐是至少保留 3 份副本、使用 2 種不同的儲存媒介,並將其中 1 份存放於異地。備份檔案統一集中放在使用者家目錄下,而不是網站根目錄,避免備份檔被外部直接下載到 💾
# 💠 單一網站
WP_PATH="/var/www/example.com/public_html"
BACKUP_DIR="$HOME/wp-security-backup"
STAMP="$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_DIR"
wp --path="$WP_PATH" db export \
"$BACKUP_DIR/site_pre_update_${STAMP}.sql"
tar -czf \
"$BACKUP_DIR/site_wp-content_pre_update_${STAMP}.tar.gz" \
-C "$WP_PATH" \
"wp-content"
ls -lh "$BACKUP_DIR"/*"${STAMP}"*
# ❇️ 獨立多站:逐站備份,檔名含站名避免互相覆蓋
SEARCH_ROOT="/var/www"
BACKUP_DIR="$HOME/wp-security-backup"
STAMP="$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_DIR"
find "$SEARCH_ROOT" -type f -name "wp-config.php" -print0 |
while IFS= read -r -d '' config; do
site_path="$(dirname -- "$config")"
site_name="$(basename -- "$site_path")"
owner="$(stat -c '%U' -- "$config" 2>/dev/null || echo 'www-data')"
printf '=== 備份:%s ===\n' "$site_name"
sudo -u "$owner" -- wp --path="$site_path" db export \
"$BACKUP_DIR/${site_name}_pre_update_${STAMP}.sql" 2>/dev/null
tar -czf "$BACKUP_DIR/${site_name}_wp-content_pre_update_${STAMP}.tar.gz" \
-C "$site_path" "wp-content" 2>/dev/null
printf '完成:%s\n' "$site_name"
sleep 3
done
# ✴️ Multisite:db export 是整個網路共用資料庫,不是單一 Site
WP_PATH="/var/www/network.example.com/public_html"
BACKUP_DIR="$HOME/wp-security-backup"
STAMP="$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_DIR"
wp --path="$WP_PATH" db export \
"$BACKUP_DIR/network_pre_update_${STAMP}.sql"
tar -czf \
"$BACKUP_DIR/network_wp-content_pre_update_${STAMP}.tar.gz" \
-C "$WP_PATH" \
"wp-content"
🚨 提醒:獨立多站的迴圈裡加了 sleep 3,這不是隨手加的裝飾——如果一次性對所有網站並行備份,磁碟 I/O 跟資料庫連線可能會被瞬間打爆,逐站加入緩衝雖然比較慢,但每一站都在可控狀態下完成,出事也好定位是哪一站的問題 🐢
❷ Dry-run:先看更新會做什麼,不真的動手
# 💠 單一網站 WP_PATH="/var/www/example.com/public_html" wp --path="$WP_PATH" plugin update shortpixel-image-optimiser --dry-run
預期輸出範例:
Plugin shortpixel-image-optimiser 6.5.4 will be updated to 6.5.6.
分支判斷邏輯:看到目標版本 → 可以進入正式更新;顯示沒有可用更新 → 回頭確認目前版本與更新來源,不要因為「No updates」就直接認定網站安全,有可能是外掛已經是最新版,也可能是官方套件庫連線異常 🔮
❸ 更新:確認備份與 Dry-run 都沒問題才真正修改
# 💠 單一網站:三部曲 deactivate → update → activate
WP_PATH="/var/www/example.com/public_html"
wp --path="$WP_PATH" plugin deactivate shortpixel-image-optimiser
wp --path="$WP_PATH" plugin update shortpixel-image-optimiser
wp --path="$WP_PATH" plugin activate shortpixel-image-optimiser
wp --path="$WP_PATH" plugin get shortpixel-image-optimiser --field=version
# ❇️ 獨立多站:逐站完成備份→更新→驗證,再進入下一站
SEARCH_ROOT="/var/www"
find "$SEARCH_ROOT" -type f -name "wp-config.php" -print0 |
while IFS= read -r -d '' config; do
site_path="$(dirname -- "$config")"
owner="$(stat -c '%U' -- "$config" 2>/dev/null || echo 'www-data')"
printf '=== 處理網站:%s ===\n' "$site_path"
sudo -u "$owner" -- wp --path="$site_path" plugin deactivate shortpixel-image-optimiser 2>/dev/null
sudo -u "$owner" -- wp --path="$site_path" plugin update shortpixel-image-optimiser 2>/dev/null
sudo -u "$owner" -- wp --path="$site_path" plugin activate shortpixel-image-optimiser 2>/dev/null
printf '完成:%s\n' "$site_path"
sleep 3
done
# ✴️ Multisite:外掛檔案只需更新一次,會影響全網
WP_PATH="/var/www/network.example.com/public_html"
wp --path="$WP_PATH" plugin update shortpixel-image-optimiser --network
wp --path="$WP_PATH" plugin get shortpixel-image-optimiser --field=version
🧑🔧 多站環境的實務原則:一次只處理一個網站,至少在第一批部署時如此。先完成一站的「備份 👉 更新 👉 驗證」,確認沒有 PHP Fatal Error、白畫面或登入異常,再繼續下一站,這比一個巨大迴圈從頭跑到尾更容易控制風險 🎛️
❹ 驗證:更新成功 ≠ 工作完成,至少確認三層
WP_PATH="/var/www/example.com/public_html"
# ❶ 版本層:確認已經是修補後的版本
wp --path="$WP_PATH" plugin get shortpixel-image-optimiser --field=version
# ❷ 檔案完整性層:跟官方發布版本比對 Checksum
wp --path="$WP_PATH" plugin verify-checksums shortpixel-image-optimiser
wp --path="$WP_PATH" core verify-checksums
# ❸ 前後台功能層
curl -sS -o /dev/null -w "%{http_code}\n" "https://example.com/"
wp --path="$WP_PATH" plugin list --status=active --fields=name,status,version | grep shortpixel
預期輸出範例:
# ❶ 的輸出 6.5.6 # ❷ 的輸出 Success: Verified 1 of 1 plugins. # ❸ 的輸出 200
🚨 Checksum 驗證的侷限:它只能證明「外掛目錄裡的檔案內容跟官方發布版一致」,無法證明整個網站沒有後門——它檢查不到 mu-plugins 目錄、資料庫裡的異常內容,也檢查不到其他外掛或主題是否被動過手。因此 Checksum 驗證應該被當成完整性檢查的其中一環,而不是唯一的安全保證,建議搭配第五章的後門檢查一起做 🔐
🔥 一句話總結:真正安全的批次維運,是先盤點,再備份,再 Dry-run,確認無誤後才逐批更新,最後驗證。環境判斷錯誤比指令寫錯更致命——這套判斷環境 👉 盤點 👉 備份 👉 Dry-run 👉 更新 👉 驗證的閉環,換成下一次別的外掛、別的 CVE,依然可以直接套用 🦸
🧩 舉一反三:這一整類漏洞的通用防禦知識庫
這次的 CVE-2026-17086 屬於典型的不可信資料反序列化(CWE-502)類型。以下是這一大類漏洞的通用防禦招式,不是本 CVE 的官方要求,而是給想舉一反三的 DevOps 參考:
- ⭐ 優先用 JSON 取代 PHP 原生序列化。儲存結構化資料時,能用 json_encode/json_decode 就別用 serialize/unserialize,JSON 天生不會被還原成任意物件,從根本上阻斷 POP 鏈的成立條件。
- ⭐ 非得用反序列化時,一定要限制可還原的類別。PHP 7 以後的 unserialize() 支援第二個參數 [‘allowed_classes’ => false],或是明確列出白名單類別,別讓攻擊者控制的字串能還原成任意類別的物件。
- ⭐ 用最小權限原則收斂攻擊面。定期審視誰擁有作者以上的權限,把不再需要的帳號降級或停用;每一個額外的外掛跟主題,都可能成為別人 POP 鏈的來源,用不到的就移除。
🏆 第七章:事件綜合評估——這次到底該多緊張?
| 評估維度 | 分數 | 一句話評語 |
|---|---|---|
| 漏洞嚴重程度(越高代表風險越大) | 8.8 | 要湊條件才會真正爆炸,但條件在多作者站上並不難湊齊 |
| 官方應變速度 | 8 | 從通報到補丁上線約 6 週,且補丁比公開細節整整早了 8 天,走的是很扎實的負責任揭露節奏 |
| 一般站長自救可行性 | 9 | 點更新按鈕就能解決,操作門檻不高,唯一需要多做一步的是盤點作者帳號 |
| DevOps 技術文件完整度 | 7 | 攻擊鏈路與 CVSS 向量清楚,但官方尚未公開完整修補 diff,成因分析仍有部分待官方進一步證實 |
| 🏆 加權綜合建議 | 近期內完成更新 | 最終建議:【本週內完成升級,開放投稿站建議提前處理】 |
🔥 這起事件提醒我們一件事:漏洞不見得都躲在複雜的核心系統裡,有時候反而藏在一支「一直很正常在運作」的小工具背後。ShortPixel 這種級數的圖片壓縮外掛,多數人裝上去就再也沒點開過它的設定頁——但只要它還「啟用」著,就一直是整體攻擊面的一部分。與其等哪天真的湊巧被串成大事,不如現在就花五分鐘,打開後台看一眼版本號碼 🔢
📚 參考資料與延伸資源
- 📌 Wordfence Threat Intelligence|ShortPixel Image Optimizer ≤ 6.5.5 Authenticated PHP Object Injection
- 📌 NVD|CVE-2026-17086 官方詳情頁
- 📌 CVE.org 官方紀錄|CVE-2026-17086
- 📌 VulDB|CVE-2026-17086 技術分析
- 📌 WordPress.org 外掛頁面與更新日誌|ShortPixel Image Optimizer
- 📌 WPScan|ShortPixel Image Optimizer 歷史漏洞紀錄
- 📌 Wordfence|2024 年度 WordPress 資安報告(PDF)
- 📌 WP-CLI 官方指令文件
- 📌 WP-CLI 官方文件|wp config shuffle-salts










