跳至主要內容
>_ITDITD網站資安平台

資安指南

Node.js 的原型污染:執行環境不負責的部分,以及如何在自己的程式碼中防禦

外部資料中的特殊鍵經過遞迴合併後,會讓你從未設定的屬性出現在毫不相關的物件上。Node.js 表明這不是核心的漏洞,所以防禦是你的責任。

發布於 2026-09-05 更新於 2026-10-04 最後核實 2026-09-05 閱讀時間 3 分鐘

你接收了外部的資料,之後你從未設定的屬性開始出現在毫不相關的物件上。或者 hasOwnProperty 這類標準方法突然變成「not a function」,應用程式就此當掉。兩者都是原型污染的症狀。

Node.js 負責的,與留給你的

Node.js 明說這不是它的漏洞

Node.js 官方安全指南寫得很清楚:「依照 Node.js 的威脅模型,仰賴攻擊者控制使用者輸入的原型污染,不被視為 Node.js 核心的漏洞,因為 Node.js 信任應用程式碼提供的輸入。」

緊接著又寫:「儘管如此,原型污染對 Node.js 應用程式和第三方函式庫而言是嚴重的漏洞類型,你應該在應用程式與相依套件層級實作防禦。」

這不是矛盾,而是在說明責任落在哪裡。執行環境站在不保證輸入格式正確的那一邊。也就是說,「等 CVE、等更新」在這裡行不通。這又是本站一再遇到的情況:你以為有人在處理的部分,其實沒有人負責。

會出什麼問題(兩種結果)

外部輸入(JSON、查詢字串、表單)

夾帶特殊鍵

↓ 遞迴合併/深層複製/組合設定

共用的範本(原型)被改寫

↓ 所有繼承它的物件也一樣

1. 出現你從未設定的值

權限旗標與選項判斷出錯

2. 內建方法消失

應用程式在無關之處當掉(DoS)

原本只針對一個物件的寫入,會影響所有共用該範本的物件。這就是損害出現在無關之處的原因。
兩種結果,用維運的角度來看
1. 屬性注入
「不可能被設定」的值變得讀得到。凡是選項物件或權限旗標只靠存在與否來判斷的地方,被注入的值就會直接被相信。若觸及授權判斷,就成了權限問題;若觸及範本或指令的組合,後果可能嚴重得多
2. 阻斷服務
範本本身被破壞後,hasOwnProperty 這類內建方法會丟出「is not a function」錯誤。文件把這列為可能的 DoS。值得記住的是:這不只是權限提升的問題
影響範圍
文件舉的例子是 Node.js 本身(CVE-2022-21824)和第三方函式庫(Lodash,CVE-2018-3721)。自己沒寫遞迴合併,但相依套件寫了,結果還是一樣

文件列出的緩解措施(七項)

Node.js 把它們一一列了出來。依用途分組會比較好套用:在入口擋下、讓結構無法被污染、改變讀取方式。

在入口擋下

・避免不安全的遞迴合併(文件舉了 CVE-2018-16487)
・對外部與不受信任的請求實作 JSON Schema 驗證

讓非預期的鍵根本進不來,是最可靠的路。請重新檢視那些接收整個深層物件、再合併進既有狀態的設計。

改變結構與讀取方式

・用 Object.create(null) 建立沒有原型的物件(最適合當字典)
・用 Object.freeze(MyObject.prototype) 凍結原型
・用 Node 的 --disable-proto 旗標停用 Object.prototype.proto
・用 Object.hasOwn(obj, key) 確認屬性是物件自己的
・避免使用 Object.prototype 的方法

效果最大的改動是存在判斷的寫法

實務上效果最廣的是改用 Object.hasOwn(obj, key)。單純的存在判斷(這個屬性存在嗎?)對從範本繼承來的值也會回答「是」,被注入的值正是這樣被撿起來的。確認屬性是物件自己的,光這一點就能防住結果 1 的大部分情況。

基於同樣的理由,避免直接從物件呼叫 obj.hasOwnProperty(key),因為這個方法正是污染可能移除的東西之一(結果 2)。

在自己的程式碼中要檢查什麼

1

你是否把外部輸入當作深層物件接收?

請求本文、查詢字串、表單、webhook 酬載:找出所有接收整個巢狀結構並合併進既有狀態的地方。外部輸入只會從這裡進來。用 schema 明確決定接受哪些鍵,是最短的修正。

2

改變字典型物件的建立方式

凡是當作「鍵在執行時才決定的容器」使用的物件(以 ID 為鍵的對照表、設定集合),都應該用 Object.create(null) 建立。沒有範本,就沒有東西可繼承。用結構解決原本得靠驗證解決的問題。

3

存在判斷統一用 Object.hasOwn

搜尋依賴「屬性是否存在」的分支,並改寫它們。權限、旗標、選項的判斷優先,它們直接關係到授權的實作方式。

4

讓機器監看相依套件

自己沒寫遞迴合併,但相依套件寫了,結果還是一樣,這也是文件舉的例子包含第三方函式庫的原因。相依套件的已知漏洞無法靠人工追蹤,請像 osv-scanner 入門那樣交給機器監看。被污染的相依套件帶來的更廣泛風險,請見 npm 供應鏈攻擊的防禦。

5

用同樣的眼光看伺服器框架的入口

每個 Node 框架都有把請求本文反序列化的路徑。我們查看 NVD 資料時,Next.js 的漏洞集中在伺服器端的職責:快取、middleware、Server Actions(Next.js 和 React 的漏洞很多嗎?)。先拋開**「這是前端函式庫,跟這個無關」**的想法。

本站的看法:責任範圍寫得明確的地方,要自己去確認

這篇文章在維運上最有用的,不是緩解措施清單,而是說明這不被視為 Node.js 核心漏洞的那句話。責任範圍寫得這麼清楚時,範圍外的事就沒有人處理:不會有 CVE,也不會有更新來修。

我們把它看成和以下問題同一類:憑證擋不住的接管(子網域接管),以及不會刪除底下既有政策的封鎖設定(公開的儲存貯體)。**你以為有人負責的地方,實際上是誰在負責?**回答一次這個問題,優先順序往往會大幅改變。

資料來源(一手資料)

  • Node.js「Security Best Practices」(Prototype Pollution Attacks/CWE-1321 一節)— nodejs.org(威脅模型的立場、兩種結果、引用的 CVE 和七項緩解措施,全部出自這份文件;2026 年 9 月 5 日確認)

接著讀

FAQ

Q原型污染是 Node.js 的 bug 嗎?更新就會修好嗎?
A

不會有更新來修。Node.js 官方安全指南表明,依照 Node.js 的威脅模型,仰賴攻擊者控制使用者輸入的原型污染,不被視為 Node.js 核心的漏洞,因為 Node.js 信任應用程式碼提供的輸入。接著又說:儘管如此,原型污染對 Node.js 應用程式和第三方函式庫而言是嚴重的漏洞類型,你應該在應用程式與相依套件層級實作防禦。依設計,這是你的責任。

Q實際上會出什麼問題?
A

有兩種結果。第一,你從未設定的屬性開始出現在整個程序的物件上。如果權限旗標或選項只靠「存在與否」來判斷,被注入的值就會被相信,授權或業務邏輯就會出錯。第二,阻斷服務:內建原型被破壞後,hasOwnProperty 這類標準方法會丟出「is not a function」錯誤,讓與該輸入毫無關係的程式碼當掉。第二種結果說明,這不只是權限提升的問題。

Q要在哪裡修?
A

在入口和資料結構。在入口,對外部與不受信任的請求套用 JSON Schema 驗證,讓非預期的鍵根本進不來。在結構上,停止使用不安全的遞迴合併,並用 Object.create(null) 建立字典型物件,讓它完全沒有原型。讀取時,用 Object.hasOwn(obj, key) 確認屬性是物件自己的,並避免依賴 Object.prototype 的方法。在執行面上,還有 Object.freeze 和 Node 的 --disable-proto 旗標。

Q我沒寫過遞迴合併,這樣就安全了嗎?
A

不是。文件舉的例子同時包括 Node.js 本身(CVE-2022-21824)和第三方函式庫(Lodash,CVE-2018-3721)。設定合併、查詢字串解析、表單反序列化,都是常用函式庫替你組出深層物件的地方。所以防禦不會只在你自己的程式碼裡完成,還要搭配用機器監看相依套件。