你接收了外部的資料,之後你從未設定的屬性開始出現在毫不相關的物件上。或者 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)。
在自己的程式碼中要檢查什麼
你是否把外部輸入當作深層物件接收?
請求本文、查詢字串、表單、webhook 酬載:找出所有接收整個巢狀結構並合併進既有狀態的地方。外部輸入只會從這裡進來。用 schema 明確決定接受哪些鍵,是最短的修正。
改變字典型物件的建立方式
凡是當作「鍵在執行時才決定的容器」使用的物件(以 ID 為鍵的對照表、設定集合),都應該用 Object.create(null) 建立。沒有範本,就沒有東西可繼承。用結構解決原本得靠驗證解決的問題。
存在判斷統一用 Object.hasOwn
搜尋依賴「屬性是否存在」的分支,並改寫它們。權限、旗標、選項的判斷優先,它們直接關係到授權的實作方式。
讓機器監看相依套件
自己沒寫遞迴合併,但相依套件寫了,結果還是一樣,這也是文件舉的例子包含第三方函式庫的原因。相依套件的已知漏洞無法靠人工追蹤,請像 osv-scanner 入門那樣交給機器監看。被污染的相依套件帶來的更廣泛風險,請見 npm 供應鏈攻擊的防禦。
用同樣的眼光看伺服器框架的入口
每個 Node 框架都有把請求本文反序列化的路徑。我們查看 NVD 資料時,Next.js 的漏洞集中在伺服器端的職責:快取、middleware、Server Actions(Next.js 和 React 的漏洞很多嗎?)。先拋開**「這是前端函式庫,跟這個無關」**的想法。
資料來源(一手資料)
- Node.js「Security Best Practices」(Prototype Pollution Attacks/CWE-1321 一節)— nodejs.org(威脅模型的立場、兩種結果、引用的 CVE 和七項緩解措施,全部出自這份文件;2026 年 9 月 5 日確認)
接著讀
- 同類問題:不安全的反序列化(由外部資料決定型別)
- 相依套件:osv-scanner 入門/npm 供應鏈攻擊的防禦
- 整體情況:Next.js 和 React 的漏洞很多嗎?(伺服器端會出現什麼)
- 設計:驗證與授權的差別(讓被注入的值永遠到不了權限判斷)
FAQ
Q原型污染是 Node.js 的 bug 嗎?更新就會修好嗎?
不會有更新來修。Node.js 官方安全指南表明,依照 Node.js 的威脅模型,仰賴攻擊者控制使用者輸入的原型污染,不被視為 Node.js 核心的漏洞,因為 Node.js 信任應用程式碼提供的輸入。接著又說:儘管如此,原型污染對 Node.js 應用程式和第三方函式庫而言是嚴重的漏洞類型,你應該在應用程式與相依套件層級實作防禦。依設計,這是你的責任。
Q實際上會出什麼問題?
有兩種結果。第一,你從未設定的屬性開始出現在整個程序的物件上。如果權限旗標或選項只靠「存在與否」來判斷,被注入的值就會被相信,授權或業務邏輯就會出錯。第二,阻斷服務:內建原型被破壞後,hasOwnProperty 這類標準方法會丟出「is not a function」錯誤,讓與該輸入毫無關係的程式碼當掉。第二種結果說明,這不只是權限提升的問題。
Q要在哪裡修?
在入口和資料結構。在入口,對外部與不受信任的請求套用 JSON Schema 驗證,讓非預期的鍵根本進不來。在結構上,停止使用不安全的遞迴合併,並用 Object.create(null) 建立字典型物件,讓它完全沒有原型。讀取時,用 Object.hasOwn(obj, key) 確認屬性是物件自己的,並避免依賴 Object.prototype 的方法。在執行面上,還有 Object.freeze 和 Node 的 --disable-proto 旗標。
Q我沒寫過遞迴合併,這樣就安全了嗎?
不是。文件舉的例子同時包括 Node.js 本身(CVE-2022-21824)和第三方函式庫(Lodash,CVE-2018-3721)。設定合併、查詢字串解析、表單反序列化,都是常用函式庫替你組出深層物件的地方。所以防禦不會只在你自己的程式碼裡完成,還要搭配用機器監看相依套件。