多數人想像駭客闖進 WordPress 後台,第一步得先偷到帳號密碼,或至少騙到管理員把密碼打進一個假登入頁。CSRF攻擊完全不是這麼回事,它連密碼都不必碰,借用的是瀏覽器裡那個「還記得你已經登入」的狀態,讓後台以為這是你自己在操作。
你可能什麼異狀都沒發現,只是點開一封信裡的連結,或瀏覽了一個看起來無害的網頁,會員的預設註冊角色卻已經悄悄被改成管理員,外掛的某個開關也被打開。CSRF(Cross-Site Request Forgery,跨站請求偽造)攻擊,鎖定的正是這種會改變資料庫內容的後台操作,改設定、刪文章、加使用者,這些動作只要能被觸發一次,傷害就已經造成。WordPress 核心確實內建了叫 nonce 的驗證機制來擋這類請求,但它不是裝了就萬無一失的盾牌,有兩個常被忽略的限制。
先從瀏覽器怎麼在你不知情的狀況下代替你發出請求講起,再看 nonce 擋得住什麼、擋不住什麼。
CSRF 是什麼?
當你登入 WordPress 後台,瀏覽器會收到一段用來證明「這個人已經通過驗證」的 cookie,之後每一次造訪這個網站,瀏覽器都會自動把這段 cookie 附加在請求裡,不需要每次重新輸入帳號密碼。這個自動帶上身分證明的設計原本是為了方便,卻也留下一個破口,伺服器收到請求時只看得到 cookie 合不合法,看不出這個請求是使用者主動點的,還是瀏覽器在不知情的狀況下被誘導送出的。
根據 OWASP 對 CSRF 的定義,這是一種迫使已經通過驗證的使用者,在自己信任的網站上執行非本意動作的攻擊;如果受害者是一般使用者,CSRF 可能造成轉帳、改密碼、改電子郵件等未經授權的狀態變更,如果受害者是管理員帳號,攻擊者足以掌控整個應用程式。攻擊者不需要看到、也不需要偷到任何密碼或資料,只是借用受害者已經被信任的身分去下指令。
這也解釋了為什麼 CSRF 專打會改變資料的操作,而不是用來偷資料。攻擊者強迫受害者的瀏覽器送出請求,收到回應的仍然是受害者自己的瀏覽器,攻擊者本人完全看不到伺服器回傳了什麼內容,單純讀取一頁資料對攻擊者沒有意義,唯一划算的做法是讓這個請求順便改動一些東西。WordPress 官方開發文件在解釋為什麼要有 nonce 機制時,舉過一個很直白的例子。後台刪除文章的連結,格式類似 wp-admin/post.php?post=123&action=trash(123 是文章編號);如果這一類連結沒有額外的驗證機制,只要瀏覽器帶著有效的登入 cookie 造訪這個網址,WordPress 就會信任並執行刪除。攻擊者可以把這種網址包成一張看不見的圖片,或偽裝成一個連結,藏在第三方頁面或信件裡,管理者只要瀏覽器仍保持登入、點到那個連結或載入那個頁面,刪除動作就有可能在不知情下被觸發,這正是 WordPress 要在每個異動性連結上額外加驗證碼的原因。

登入中的管理者瀏覽器才是攻擊者真正鎖定的目標
WordPress 後台之所以特別容易成為 CSRF 的目標,不是因為 WordPress 這套系統特別脆弱,而是後台操作的破壞力跟觸發門檻嚴重不成比例。管理員登入一次之後,瀏覽器會在效期內對每個請求自動附上驗證 cookie,不必每次重新輸入密碼;攻擊者不必攻破密碼、也不必闖過防火牆,只要讓已經登入的管理員瀏覽器在無意間送出一個請求就夠了。後台操作往往牽動整個網站的核心設定,改全站選項、加一個新的管理員帳號、動到主題檔案的內容,這些能造成的破壞遠遠超過一個一般會員帳號被盜用的規模。
瀏覽器自動附帶 cookie 這件事,不是 WordPress 獨有的設計,而是所有靠 cookie 驗證身分的網站共同的弱點。任何一個網站,只要用 cookie 記住登入狀態,理論上都可能被 CSRF 盯上,差別只在於這個網站上有多少操作屬於登入後就能改變資料的高風險動作,以及這些操作有沒有另外加驗證。管理員帳號的操作範圍最大,因此在任何靠 cookie 驗證的系統裡,都是最有價值的攻擊目標。
攻擊者要騙到管理員瀏覽器送出請求,靠的通常不是技術入侵,而是社交工程。根據 OWASP 的說明,CSRF攻擊分兩個步驟:先建立一個惡意的網址或腳本,再透過社交工程誘騙受害者觸發它;常見的手法是不告知使用者,把偽造請求藏在會被自動載入的圖片標籤或表單裡,讓瀏覽器在背景自動送出請求,使用者完全無感。具體的形式包括:寬高設為 0、肉眼看不到的隱藏圖片標籤;偽裝成正常連結的信件或留言;用 JavaScript 在頁面載入時自動觸發表單送出,不需要受害者按下任何按鈕。這些手法的共通點是,受害者什麼都沒主動做,或只是點了一個看似無害的連結,攻擊就已經完成。
CSRF 與 XSS 的差別在於偽造請求還是植入程式碼
站上另一種常被提到的威脅是 XSS(跨站腳本攻擊),兩者常常被搞混。核心差異在於網站信任的對象不同,XSS 是攻擊者把惡意程式碼偷偷植入網站,讓程式碼在其他訪客的瀏覽器裡執行,利用的是網站對使用者輸入內容的信任;CSRF 則是攻擊者偽造一個請求,借用受害者已經被網站信任的登入身分去執行動作,利用的是網站對已登入瀏覽器的信任。攻擊者要成功發動 CSRF,不需要在網站上植入任何程式碼,借用的是受害者已經存在的權限。

這個差異也決定了兩者需不需要在目標網站留下痕跡。XSS 需要成功把程式碼寫進網站的某個角落,例如留言欄位或表單輸入,才有機會執行;CSRF 完全不用,靠的是受害者自己瀏覽器裡那個真實存在的登入狀態,攻擊本身發生在受害者的瀏覽器端,而不是伺服器端被動了手腳。
但兩者並非完全互不相干。OWASP 的 CSRF 防護指南明白點出一句話:「跨站腳本(XSS)可以擊敗所有的 CSRF 防護手段。雖然 XSS 漏洞能繞過 CSRF 保護,CSRF 驗證碼對仰賴 cookie 做身分驗證的網頁應用程式仍然是必要的。」這句話同時點出兩者本質不同,一個防偽造請求、一個防惡意程式碼被執行,也點出彼此的關聯,網站若同時存在 XSS 漏洞,CSRF 的防護就可能形同虛設。
Nonce 驗證碼,WordPress 核心內建的請求核對機制
WordPress 對抗 CSRF 的預設武器叫 nonce。WordPress 會在每一個會異動資料的後台連結、表單、AJAX 請求裡,夾帶一段由使用者、時間、要執行的動作算出來的驗證碼;請求送回伺服器時,WordPress 會核對這段驗證碼合不合法、是不是配對這個使用者與這個動作,不合法就直接擋下,回應 403 拒絕存取。這是 WordPress 核心內建、不必額外安裝任何外掛就有的機制,幾乎每個會寫入資料庫的後台操作背後都靠它把關。
不過 nonce 這個名字容易造成誤解。它是「number used once(只用一次的號碼)」的縮寫,聽起來像是用過一次就作廢,但技術上 WordPress 的 nonce 並不是真正的一次性,同一個使用者、同一個動作,在效期內會重複拿到同一個 nonce,可以多次驗證通過,不是用過一次就失效。另一個常被忽略的限制是,nonce 只驗證這個請求是不是使用者本人在這個網站上主動發起的,不驗證使用者有沒有權限做這件事,這是網站要分開把關的兩道關卡,WordPress 官方文件也提醒,nonce 不應該被用來做身分驗證、授權或存取控制,功能本身還是要另外用權限檢查函式保護,並且永遠假設 nonce 有可能被破解。

Nonce 有效期最長一天且綁定使用者與特定操作
WordPress 官方文件把 nonce 的預設壽命定在一天,時間一到,就算格式完全正確也視為失效。開發者可以透過設定調整效期長短,但一般站主不需要自己動這個設定,理解它不是永久有效就夠。
而且每個 nonce 是綁定特定使用者加特定動作產生的,不能拿甲功能用剩的 nonce 去驗證乙功能,也不能被其他使用者的瀏覽器拿去用。這代表就算攻擊者手上真的握有一段合法的 nonce,也只能用在它原本對應的那個使用者、那個動作上,沒辦法直接套用到別的地方,這也是 nonce 機制真正能擋下大部分偽造請求的原因。
站內若有 XSS 漏洞,Nonce 防護形同虛設
前面提到,XSS 可以擊敗所有 CSRF 防護手段,原理其實不複雜。nonce 驗證碼本身就顯示在頁面的原始碼或 JavaScript 變數裡,是公開可見的內容;如果網站有 XSS 漏洞,攻擊者能讓惡意程式碼在受害者,例如管理員自己的瀏覽器裡執行,這段程式碼可以直接讀出頁面上正確的 nonce,再用這個合法的 nonce 發出偽造請求。
因為請求是從受害者自己的瀏覽器、用受害者自己拿到的合法 nonce 送出的,WordPress 完全無法分辨這是不是本人的意願。nonce 能擋外部直接偽造的請求,卻擋不住網站自己先被 XSS 打穿這個前提被破壞的情況。這也是為什麼站主不能只裝一種防護就覺得安全,nonce 跟避免 XSS 是要一起做的兩件事,少一件都可能被繞過。
帳號權限、外掛設定與檔案編輯功能的偷改路徑
講完機制,回到多數站主真正關心的問題,CSRF 在 WordPress 上實際能造成什麼後果。CSRF攻擊的目標多半是會寫入資料庫的設定選項,像是會員預設角色、外掛開關、重導向網址、佈景檔案內容,都可能被鎖定。攻擊者不需要事先取得任何帳號密碼,只需要目標網站的某個功能忘記做 nonce 驗證,後台設定被偷改不是危言聳聽,輕則帳號權限被動手腳,重則整站淪陷。
LoginPress 外掛漏洞讓網站選項遭竄改,攻擊者藉此取得管理員權限
官方 CVE 紀錄 CVE-2025-1764 記載,WordPress 外掛 LoginPress(一款登入頁面客製化外掛)3.3.1 及以前的版本,外掛裡一個負責寫入設定選項的功能缺少 nonce 驗證。在外掛開啟開發模式(開發用的特定常數被設為啟用)的前提下,未經授權的攻擊者只要誘騙已登入的網站管理員點擊一個偽造連結,就能透過偽造請求任意更新整個 WordPress 網站的設定選項,包含把預設使用者註冊角色改成管理員、並打開開放使用者註冊這個開關。
改完這兩個設定後,攻擊者自己到註冊頁面申請一個帳號,新帳號就會直接拿到管理員權限,等於不用密碼、不用另外的登入漏洞,純粹靠一次被偷改的設定就拿下整個網站的控制權。這個漏洞的 CVSS 3.1 評分是 7.5,屬於高風險等級,已在外掛 4.0.0 版修補。CSRF 真正可怕的地方,不是攻擊本身有多精密,而是它把改一個設定跟拿下整個帳號體系串在一起,一步就跨過了原本需要密碼才能跨過的門檻。
WordPress 核心曾出現的 CSRF 串接程式碼執行漏洞
另一個案例不是第三方外掛,而是 WordPress 核心本身曾出現的漏洞,示範 CSRF 串接其他漏洞後,能一路捅到整站被接管、能在伺服器上執行任意程式碼的程度。WordPress 在 2019 年 3 月發布的 5.1.1 資安維護版公告寫明,這個版本修補了一組處理留言篩選與儲存方式的資安漏洞,用一則刻意構造過的留言,WordPress 文章會出現跨站腳本風險,此問題影響 5.1 及以前的所有版本;官方並向發現者致謝,感謝他們私下通報,讓官方有時間在網站被攻擊前完成修補。
這個漏洞登記為 CVE-2019-9787。根據發現者、Sonar(原 RIPS Technologies)研究員 Simon Scannell 發表的技術分析,在有開放留言的網站上,已登入的管理員只要造訪攻擊者架設的網頁,頁面就會在背景對目標網站送出偽造請求,以管理員的身分發表一則留言;當時發表留言這個動作沒有做 CSRF 驗證,加上留言內容的篩選有缺陷,留言夾帶的惡意程式碼被存進資料庫,成為儲存型 XSS,攻擊者再用隱藏的頁框讓它在管理員的瀏覽器裡執行。因為 WordPress 預設允許管理員直接在後台編輯佈景與外掛的檔案內容,攻擊者能藉此寫入一段後門程式碼,取得在伺服器上執行任意程式碼的能力,等於從一個看似無害的網頁、一則偽裝留言,一路串到整台伺服器被接管。
多數 CSRF 漏洞其實敗在忘記更新
前面兩個案例的共通點,都不是 WordPress 沒有 nonce 機制,而是某個功能忘記檢查 nonce。LoginPress 的漏洞在 4.0.0 版修補,WordPress 核心的漏洞在 5.1.1 版修補,兩者共通的結局都一樣,漏洞被發現後,官方或外掛作者盡快釋出修補版本,沒有更新的網站繼續暴露在風險中。這代表站主能做、而且最有效的第一件事,就是讓核心與外掛保持在最新版本,別讓已經被公開、已經有修補方案的漏洞繼續留在自己的網站上。
實務上這件事拖延的機率很高,開啟自動更新,或至少別拖太久才手動更新,是最基本也最有效的第一步。挑選外掛時,近期是否仍有更新紀錄、開發者對已知資安通報是否有回應,也是判斷外掛品質的重要指標,回頭看 LoginPress 那個案例,它的修補版本其實在漏洞公開前就已經發布,願意持續維護的外掛,通常能把暴露的時間窗口壓到最短。
登入習慣與資安外掛是最後兩道防線
除了保持更新,還有兩塊 nonce 機制本身管不到的防線,是站主可以主動做的事。第一塊是瀏覽習慣,處理完後台事務盡快登出,別讓管理員帳號長時間保持登入狀態,常駐登入會拉長暴露窗口;也不要在登入 WordPress 後台的同一個瀏覽器分頁,隨意點開不明連結或信件附的連結,前面提過,CSRF 的觸發往往就藏在一個看似無害的連結或圖片裡。
第二塊是資安外掛裡的 WAF(網站應用防火牆)功能,能攔截明顯異常的偽造請求,是 nonce 機制之外多一層防護,但不能取代保持更新這個根本做法,只能當成最後一道補漏網。另外值得知道的是,現代瀏覽器本身也提供一層被動防護,Chrome、Edge 等部分主流瀏覽器已把 SameSite=Lax 訂為 cookie 沒有明確指定時的預設行為,會限制部分跨站請求自動夾帶登入 cookie。但這是瀏覽器層級的通用防護,不是站主能額外設定或控制的東西,也有它的限制,Lax 模式仍會放行安全方法的頂層跳轉請求,判斷範圍是整個可註冊網域而不是精確的單一網址來源,部分舊版瀏覽器也不支援這項屬性,所以不能單獨依賴它。
CSRF 能得逞,靠的從來不是高深的技術,而是一個很平凡的假設被利用,瀏覽器記得你,伺服器就信任你。這個假設本身沒有錯,錯的往往只是某個功能忘記多驗一次這個請求真的是使用者要的嗎。下一次看到外掛更新通知跳出來,或後台無故多了一個陌生帳號,值得多想一步,問題可能不在密碼夠不夠複雜,而在那個小小的驗證碼有沒有補上。
