[英]How to prevent prototype pollution in JavaScript
最近我偶然發現了doT.js 中的一個漏洞。 該漏洞的存在是因為攻擊者可以使用原型污染來修改傳遞給 doT 的選項的值。
例子:
var doT = require("dot");
var tempFn = doT.template("<h1>Here is a sample template " +
"{{=console.log(23)}}</h1>");
tempFn({})
var doT = require("dot"); // prototype pollution attack vector
Object.prototype.templateSettings = {varname:"a,b,c,d,x=console.log(25)"};
// benign looking template compilation + application
var dots = require("dot").process({path: "./resources"});
dots.mytemplate();
然后我開始思考:這是否意味着幾乎所有JavaScript 庫的 API 選項都可能因原型污染而受到損害?
例如,這里的express.static
與選項一起使用。
var options = {
dotfiles: 'ignore',
etag: false,
extensions: ['htm', 'html'],
index: false,
maxAge: '1d',
redirect: false,
setHeaders: function (res, path, stat) {
res.set('x-timestamp', Date.now())
}
}
app.use(express.static('public', options))
攻擊者不能設置Object.prototype.redirect = true
,如果用戶未指定,會發生重定向嗎? 肯定還有更多惡意用例。
作為庫作者,可以做些什么來允許傳遞選項但防止原型污染?
編輯:我特別關注與 NPM 一起分發的包。 例如,doT.js 的作者可以做些什么來解決這個漏洞?
Olivier Arteau發布了一份完整的白皮書 PDF,稱為NodeJS 應用程序中的Prototype 污染攻擊,涵蓋了攻擊的識別和緩解。
原型污染背后的一般思想始於這樣一個事實:攻擊者至少可以控制以下形式的任何表達式的參數a
和value
:
obj[a][b] = value;
攻擊者可以將a
設置為__proto__
並且b
定義的名稱的屬性將定義在應用程序的所有現有對象(屬於obj
的類)上,值為value
。
當攻擊者至少控制a
, b
和value
時,同樣的事情可以附加以下形式。
obj[a][b][c] = value;
攻擊者可以將a
設置為constructor
, b
為prototype
, c
定義的名稱的屬性將定義在應用程序的所有現有對象上,值為value
。
然而,因為這需要更復雜的對象分配,所以第一種形式更容易使用。
雖然您很少會偶然發現在文本上看起來像提供的示例的代碼,但一些操作可以為攻擊者提供類似的控制。
它本質上用作 HashMap,但沒有Object
具有的所有安全警告。 當需要鍵/值結構時, Map
應該優先於Object
。
可以在 JavaScript 中創建沒有任何原型的對象。 它需要使用Object.create
函數。 通過此 API 創建的對象將不具有__proto__
和constructor
屬性。 以這種方式創建對象有助於減輕原型污染攻擊。
let obj = Object.create(null);
obj.__proto__ // undefined
obj.constructor // undefined
npm 上的多個庫(例如: ajv )為 JSON 數據提供模式驗證。 架構驗證確保 JSON 數據包含具有適當類型的所有預期屬性。 當使用這種方法來減輕“原型污染”攻擊時,重要的是拒絕不需要的屬性。 在ajv ,這可以通過設置來完成additionalProperties
以false
的架構。
使用Object.freeze
將減輕幾乎所有可利用的情況。
請注意,雖然向基礎對象的原型添加函數在實踐中是令人不悅的,但它仍可能在您的 Node.js 應用程序或其依賴項中使用。 強烈建議在沿着這條路線前進之前檢查您的 Node.js 應用程序及其對此類使用的依賴。 由於凍結對象的行為是在屬性分配時靜默失敗,因此可能會引入難以識別的錯誤。
Object.freeze(Object.prototype);
Object.freeze(Object);
({}).__proto__.test = 123;
({}).test // this will be undefined
作為庫作者,可以做些什么來允許傳遞選項但防止原型污染?
您可以使用.hasOwnProperty()
檢測屬性是在您的實際對象上還是通過原型繼承。 但是, .hasOwnProperty()
,攻擊者也可以覆蓋.hasOwnProperty()
並改變其行為。
正如我在評論中所說,有人在他們的 Javascript 程序中使用您的庫,可以完全訪問您的代碼。 因此,他們甚至不必使用原型污染來修改內容——他們可以隨意修改您的代碼。
為了完全保護您的代碼,您必須僅分發在不同進程中運行並具有進程間 API(例如 http 服務器)的已編譯可執行文件,或者您必須將代碼放入服務中並僅提供以這種方式訪問。 如果您要分發 Javascript 庫,就其本質而言,您必須分發源代碼,以便使用您的庫的程序員可以真正對它做任何他們想做的事情。 他們甚至不必訴諸原型技巧。
我也有這個問題,並將我的package.json
依賴項升級到最新版本包可能已過時。 有時它可能與升級或降級一樣小。
npm install -g npm-check-updates
運行命令npm audit
將為您提供依賴漏洞和建議補丁的報告。
npm audit
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.