關於js中的沙箱

2021-08-19 20:08:37 字數 2297 閱讀 8281

我的目標,非常簡單,就是希望能夠在我自己的系統中使用別人寫的**,但是這些**可能會汙染全域性變數,甚至可能是惡意的,破壞性的。我要保證這些**被正確執行,並且其影響範圍完全受到控制,這就是我想要的沙箱。

根據我自己的思考以及和一些朋友的討論,我認為我主要需要解決四點:

1.變數訪問問題:第三方可以使用變數名訪問到全域性變數。

2.this問題:函式執行時的預設this值就是全域性變數。

3.eval和function問題:eval可以動態地生成**,這些**只有到執行時才能確定。

4.literal以及自動裝箱問題: {}以及function可以構造出一些內建類的例項,這樣通過constructor和__proto__等能訪問到原生的全域性物件。

在這個問題中,我不希望引入過於重型的解決方案,比如,使用narcissus之類的js引擎去執行整個**是可行的,但是其效能極大地限制了**的能力。還有,因為一些庫和框架(如wind.js)依賴某些動態特性,將eval和function禁止也是無法接受的,甚至直接eval必須能夠訪問到其呼叫的上下文,這樣的特性也必須被保留。

變數訪問問題的解決

一些輕量級的工具(如我的jsinjs和esprima,uglifyjs等)可以解析ast(abstract syntax tree 抽象語法樹),根據抽象語法樹,可以找出所有未宣告但是已經被賦值使用的變數。

例如,以下**:

var a; 

function my()

通過ast,可以找到j 和a是被引用的全域性變數。

這個問題唯一的例外是with,with中的某些變數可能並非全域性: 

with()

因為with中的內容在執行時才能確定,所以無法預判,這裡只能按最糟糕的情況處理,認為使用了全域性s。

找到了所有被引用的全域性變數之後,只要用乙個iffe(immediately invoked function expression立即執行的函式表示式)把**套起來,並且宣告那些沒有宣告的變數,就可以把全域性變數變成區域性變數了:

void function() 

}()我們還需要暴露一些全域性的方法給第三方**使用,在iffe外面加乙個with 

with(safe_global) 

void function()

將會被變成

function f()

_$wrap函式將會檢查this是不是全域性物件,必要時將其替換成 safe_window。

因為_$wrap函式同樣在執行時做檢查,所以可以有效解決this問題。

eval和function問題的解決

eval分為直接eval和間接eval,es規範要求直接eval必須能保留呼叫時的上下文,因此實現safe_eval的方式肯定是不行了(參看《無法封裝的函式:eval》)。所幸直接eval可以從ast中直接找出來,生成的**必須仍然使用eval,我的方案是:

eval(……);

變成eval(_$check(……));

_$check函式將會在執行時遞迴地做全文中所述的ast檢查,並把結果返回,這樣直接eval的問題就得以解決了。

間接eval和function的問題類似,其**都是在全域性執行的,問題在於我們無法從ast中直接識別出來,所以還是需要執行時處理。我的方案是把safe_global中的eval變成safe_eval。

safe_global.eval  = function safe_eval();

function的情況跟間接eval差不多,不多說了。

這裡還存在乙個致命的問題,就是safe_global中的eval會阻止直接eval找到真正的eval函式。根據eval函式行為的定義:

乙個 eval 函式的直接呼叫是表示為符合以下兩個條件的 callexpression:

解釋執行 callexpression 中的 memberexpression 的結果是個 引用 ,這個引用擁有乙個 環境記錄項 作為其基值,並且這個引用的名稱是 "eval"。

以這個 引用 作為引數呼叫 getvalue 抽象操作的結果是 15.1.2.1 定義的標準內建函式。

我們可以將eval(***)變成乙個iffe。

eval(……);

變成(function() ());

這樣就儲存了上下文,這個iffe也能像eval一樣用在表示式中。

literal以及自動裝箱問題的解決

這些同樣發生在執行時,所以無法通過ast分析來解決,因為也不可能,於是我的解決方案是在乙個iframe中執行這些**。

唯一值得注意的是需要修改function.prototype.constructor到safe_function,以避免不安全的function呼叫。

關於js中的then

then 相關的東西包括但不限於 promise,thien.js 雖然還沒徹底搞清楚這些個玩意兒,但是 現在知道了 then 是幹嘛的了 最主要的,是解決了非同步方法立刻返回的問題 這個特性帶來的後遺症 假設 我要等非同步處理完了,再去執行一段 後續 怎麼做?定義乙個全域性flag,非同步處理完成...

關於 js 中 this 的理解

今天在寫乙個tab選項卡切換時遇到乙個 this 的引用問題,做個記錄加深一下理解。定時器 var timer null var lis document.getelementsbytagname li for var i 0 i 給每個li元素新增自定義屬性id,值為當前li元素在lis中的索引 ...

關於js 中的new

new 操作符 new操作符的作用 new 操作符會返回乙個物件 這個物件也就是建構函式中的 this 可以訪問到掛載在 this 上面的任意屬性 這個物件可以訪問到建構函式原型上的屬性,所以需要將物件與建構函式鏈結起來 返回原始值需要忽略,返回物件需要正常處理 手動實現 function crea...