中文字幕精品亚洲无线码二区,国产黄a三级三级三级看三级,亚洲七七久久桃花影院,丰满少妇被猛烈进入,国产小视频在线观看网站

JavaScript 沙箱(xiang)

標題圖

概述

沙(sha)箱可以簡單的(de)理解為一個(ge)(ge)(ge)虛擬機(ji),是一個(ge)(ge)(ge)和宿(su)主機(ji)隔離的(de)環境,在這個(ge)(ge)(ge)環境中去(qu)運行一些不受信任的(de)代碼或者應用程(cheng)序,防(fang)止(zhi)不安全的(de)代碼對系統造成(cheng)損害。

比如我(wo)(wo)們(men)現在(zai)知道某(mou)個(ge)(ge)應(ying)用(yong)是(shi)詐騙軟件(jian)(jian)或(huo)(huo)者病毒軟件(jian)(jian),但是(shi)我(wo)(wo)們(men)依舊想(xiang)(xiang)要運行(xing),想(xiang)(xiang)逆向分析他,那么(me)我(wo)(wo)們(men)就可(ke)以選擇(ze)在(zai)電腦上安裝一個(ge)(ge)虛擬機,在(zai)這個(ge)(ge)虛擬機中(zhong),我(wo)(wo)們(men)將(jiang)對攝像(xiang)頭(tou)的(de)訪問引導至一張靜(jing)態(tai)圖片或(huo)(huo)者視頻,將(jiang)麥克風(feng)的(de)訪問引導至一個(ge)(ge)事先錄制(zhi)好(hao)的(de)音(yin)頻文(wen)件(jian)(jian)中(zhong),通訊錄、應(ying)用(yong)列(lie)表我(wo)(wo)們(men)也提(ti)前做好(hao)“偽裝”提(ti)供給這個(ge)(ge)軟件(jian)(jian)。

上面(mian)(mian)所(suo)述的整個過(guo)程(cheng)實際(ji)就是(shi)建立沙箱的一個過(guo)程(cheng),運行在這(zhe)里面(mian)(mian)的應用(yong)所(suo)能訪(fang)(fang)問(wen)到(dao)的都是(shi)我們事先準備好的,他無(wu)法直(zhi)接訪(fang)(fang)問(wen)到(dao)我們的電腦環境,從而保證(zheng)了(le)我們不(bu)受(shou)到(dao)惡意攻(gong)擊。

計(ji)算機(ji)領域版的(de)(de)楚門的(de)(de)世界

當然(ran),電影也很好看(kan),推(tui)薦大家看(kan)看(kan)~

沙箱(xiang)機制的(de)原理非常好(hao)理解,適(shi)用性(xing)也很(hen)廣,在計(ji)算機領(ling)域中(zhong),沙箱(xiang)也存在很(hen)多種,本文僅介紹(shao) JavaScript 中(zhong)的(de)沙箱(xiang)實(shi)現。

格局打開,不要僅僅把目光放在計算機領域,沙箱本質上就是 **讓你看到我想讓你看到的東西 **,詐騙實際(ji)也是遵循這種,很(hen)刑的。

應用場景

沙箱(xiang)的應用場(chang)景十分廣泛,包(bao)括(kuo)操作(zuo)系(xi)統、網絡瀏(liu)覽器、移動應用程序(xu)等。

本文要介紹的(de) JS 沙箱通常是(shi)用(yong)于(yu) Web 瀏覽器中的(de),限制不(bu)受(shou)信任的(de)代碼(ma)的(de)訪問權(quan)限,通常認為用(yong)戶(hu)自己編(bian)寫的(de)代碼(ma)就是(shi)不(bu)受(shou)信任的(de)代碼(ma)。

服(fu)務(wu)器端也是(shi)(shi)可以(yi)使用 js 沙箱的(de),用以(yi)執(zhi)行不受(shou)信任的(de)代(dai)碼(ma),比如(ru)在 leetcode 上做(zuo)題的(de)時(shi)候,我們提交的(de)代(dai)碼(ma)是(shi)(shi)在服(fu)務(wu)器端執(zhi)行的(de),這是(shi)(shi)為了(le)防止用戶(hu)寫一些惡意的(de)代(dai)碼(ma)突破權限,對(dui)服(fu)務(wu)器造成(cheng)危害。

所以,JS 沙箱的應用主要圍(wei)繞以下兩點:

  1. 安全:解析不受信的 js 文件,防止 XSS 等
  2. 應用程序隔離:限制代碼訪問相關的對象,如彈窗廣告

詳細展開可(ke)以有(you)更多,但是大體方向(xiang)是這兩個。

實現

實現的(de)思路上,可以大致劃(hua)分為三大類(lei):IFrame、JavaScript 語言特性、快照

IFrame

特點 :瀏覽器支持(chi)的 HTML 元(yuan)素,自帶沙箱(xiang)隔(ge)離,能夠與(yu)主頁面通(tong)信。

缺點 :瀏覽(lan)器會(hui)為其單獨開啟一個子(zi)進程,有(you)額外的性(xing)能開銷。不同瀏覽(lan)器對 sandbox 屬性(xing)的支持也有(you)所(suo)不同

HTML元素,這個實際是瀏覽器支持的一種,實現會比較簡單,我們只需要使用對應的屬性 sandbox 即可,主要關注 allow-scripts 屬性, [1], 不過就個人使用情況而言,在使用 iframe 的時(shi)候(hou),這個屬性基本都處于開啟狀態,不開啟的話,在嵌入一些(xie)網站(zhan)的時(shi)候(hou)可能會顯示異常。

<iframe src="//bing.com" sandbox="allow-scripts" />

如上就(jiu)是一個比較簡單的實現,我(wo)們禁用了(le)其他(ta)的能力(li),僅啟用了(le)腳本執(zhi)行的能力(li)。

allow-scripts 僅允(yun)許執行腳本,但是無法創建彈(dan)窗這類窗口(kou)。

不過尤(you)其注意(yi),作為一(yi)種安全機制,沙(sha)箱(xiang)并不能保(bao)障絕對的安全,所以對于沙(sha)箱(xiang)中(zhong)的內(nei)容(rong),我們(men)還是(shi)需要保(bao)證(zheng)加載的內(nei)容(rong)的可(ke)信度和安全性,避免惡(e)意(yi)用戶突破或(huo)繞過沙(sha)箱(xiang)造成(cheng)攻擊(ji),導致我們(men)產生損失。

主要是存在跨站(zhan)腳本攻擊(XSS)的問題,這里(li)舉兩個(ge)例子:

  1. 竊取敏感信息:如登錄憑據,通過過濾相關標簽或者編碼來規避。
  2. 點擊劫持:釣魚網站,通常不是對網站的危害,而是對用戶的,采用禁止網站被嵌入(X-Frame-OptionsContent-Security-Policy)來規避

一般(ban)來說,我們在 iframe 中(zhong)都(dou)會訪問(wen)受信的(de)站(zhan)點,即使是彈窗(chuang)廣告,不(bu)過對(dui)于廣告,我們通常會限(xian)制一些操作,比如不(bu)允許運行腳本。如果不(bu)加以限(xian)制,他可能(neng)不(bu)會直接危害到我們的(de)站(zhan)點安全,但是可能(neng)有用戶信任下降及影響品(pin)牌聲譽的(de)問(wen)題,當有更好的(de)站(zhan)點作為替代的(de)時候,那用戶則會棄之如敝履。

目前這種彈窗廣告(gao)你(ni)已(yi)經(jing)很(hen)少能(neng)在(zai)正(zheng)規的網站上看到了,各個大(da)廠更傾向(xiang)于在(zai)信息流中加入廣告(gao)。

JavaScript 語言特性

特點:各個瀏覽器表(biao)現基本(ben)一致

缺點:性能表(biao)現(xian)與代碼實現(xian)的優劣相關

前文說過(guo),沙箱本質(zhi)是一種安全機制,是為了_** 限(xian)制第三(san)方不受信任的代碼對系(xi)統(tong)內容的訪問 **_,所以結合我們對 JavaScript 語言的了解,我們可以考慮作用域來限(xian)制訪問系(xi)統(tong)級(ji)變量。

作用域

目前市面上的編程語言,基本都有作用域的概念:在程序中定義變量的可見性和訪問范圍。 它直接決定了(le)變量(liang)的(de)生(sheng)命周期和可以訪問變量(liang)的(de)代碼片段,一般作用域[2]包括:

  1. 全局作用域(Global Scope):當前程序可以在任意位置處訪問的變量或函數,如 window。
  2. 模塊作用域(Module Scope):即 import 和 export,通常認為一個文件是一個模塊,在文件內定義的變量或函數都是該模塊私有的,如需在外部使用,則需要 export。
  3. 函數作用域(Function Scope):由函數創建的作用域,在 JavaScript 中,創建函數會為我們創建一個獨立的作用域,在 es6 之前沒有 es Module 規范時,我們使用 Function 幫助我們創建獨立的作用域來實現模塊化,即 umd 和 amd。
  4. 塊級作用域(Block Scope):es6 中引入,用一對花括號括起來的代碼塊,只對 let 和 const 聲明有效。var 聲明無效。

看到這里,不(bu)難看出(chu),作用(yong)域實際就(jiu)是一(yi)個天然的(de)沙(sha)盒,我們(men)可以(yi)這樣實現:

window; // 瀏覽器的 window 對象
window.app = 2; // 增加一個 app 字段,并將其賦值為 2

function execCode(code: string) {
  const window = null;
  ;
}

const code = 'window.app = 1';
execCode(code); // 將 window.app 的值修改為 1,執行結果:Uncaught TypeError: Cannot set properties of null (setting 'app')

可以看到,此時執行(xing)第(di)三方代碼的(de)(de)時候,這(zhe)些代碼是無法訪問我們的(de)(de) window 對象(xiang)的(de)(de),從而保證了我們的(de)(de) window 對象(xiang)的(de)(de)安(an)全。

但實際這并不是真正的安全,我們依舊有辦法能夠繞過他,比如,當我們全局定義了一個函數 updateApp(app) 的時(shi)候,我們在這里實際(ji)可(ke)以通過調(diao)用這個函(han)數的方式來(lai)繞(rao)過我們作用域的限制:

function updateApp(app: number) {
  window.app = app;
}

execCode('updateApp(1)'); // 執行成功,window.app 此時為 1

所以我們又要重申一遍:作為一種安全機制,沙箱并不能保障絕對的安全

這(zhe)里沒有考(kao)慮 with 關鍵字,因為(wei)他已(yi)(yi)(yi)經從 es5 開(kai)始(shi)的嚴格模式下就已(yi)(yi)(yi)經被(bei)禁用(yong)(yong)了,而現在由于我們使用(yong)(yong)的框架默認是(shi)以嚴格模式執(zhi)行的,所以可以說 with 關鍵字其實已(yi)(yi)(yi)經處于不可用(yong)(yong)的狀態(tai)了。新項目已(yi)(yi)(yi)經不建(jian)議(yi)使用(yong)(yong),但(dan)是(shi)老項目還(huan)在用(yong)(yong)的話,那就保持現狀吧。

不過對于這種方式實現的沙盒,我們實際可以進一步優化,引入 JavaScript 中的 new Function [3] 構造(zao)函數來執行代碼,避免一些簡單(dan)的安(an)全問題:

function execCode(code) {
  const func = new Function('window', code);
  func(null);
}

此時再執行時,你會發現,updateApp(1) 執行會報錯 ReferenceError: updateApp is not defined ,代碼字符(fu)串編(bian)譯后無法訪(fang)問我們的 updateApp 函(han)數了。

因為我們在這里構造了一個類似于 function (window) { window.app = 1 } 的函數,并(bing)在(zai)后續執行調(diao)用動作。

eval 能夠訪(fang)(fang)問(wen)(wen)本(ben)地作用域,new Function 則只能訪(fang)(fang)問(wen)(wen)全局變量和自(zi)己的局部變量,同時(shi)其構造器創建時(shi)所(suo)在的作用域的內容是無法訪(fang)(fang)問(wen)(wen)的。

Proxy 代理

看上(shang)去,Proxy 是(shi)(shi)一個比較復(fu)雜的內(nei)容,但實際上(shang)他本(ben)質上(shang)就是(shi)(shi)一個攔截器,在訪(fang)問目標內(nei)容之前,需要先經過 Proxy 幫忙去通知。

一(yi)個更貼近現實的例子,Proxy = 租(zu)房中(zhong)介(jie),你想要(yao)租(zu)房,就需要(yao)通過中(zhong)介(jie)去介(jie)紹。

如果你繞過中介直接和房(fang)東交易,當然也是可(ke)行的,因為(wei)原(yuan)始的交易對象(xiang)是你已(yi)知的。

Proxy 的一個簡單示(shi)例(li):

const windowProxy = new Proxy(window, {
  get(obj, propKey) {
    if (propKey === 'test') {
      return 'hello world';
    }
    return obj[propKey];
  }
})

windowProxy.test; // hello world,通過中介交易
window.test; // undefined,繞過中介直接和房東交易

通過上面這個簡單的例子,我們可以很輕松的看出,我們在創建出來的 Proxy 對象中限制了他訪問 test 屬(shu)性的內容,這實(shi)際上是(shi)在(zai)做(zuo)我們一(yi)開始(shi)說的,偽裝(zhuang)應用(yong)程序所需要的內容(變(bian)量等),提供給(gei)應用(yong)程序使用(yong),從而實(shi)現對(dui)惡意攻擊的防范。至(zhi)于運(yun)行(xing)代碼?那不(bu)好意思,Proxy 本身是(shi)無法像 eval 和 new Function 一(yi)樣去運(yun)行(xing)一(yi)段代碼的。

不過(guo) Proxy 方式創建沙(sha)箱也需要(yao)注意(yi):

  1. proxy 默認只會代理一級對象:也就是說,當訪問的對象是 { a: { b: { c: 1 } } }這種,用戶訪問 proxy.a.b.c其實操作的就是原對象。

基于快照的沙箱

快照(Snapshot),就是存(cun)儲某一時刻相關數據(ju)的(de)副本。

基于快照的沙箱,顧名思義,就(jiu)是在程序運(yun)(yun)行的某(mou)一個時(shi)(shi)刻(ke),或者執行某(mou)一個操(cao)作時(shi)(shi)保存當前運(yun)(yun)行環(huan)境副本,再后續的某(mou)一個時(shi)(shi)刻(ke)恢復原運(yun)(yun)行環(huan)境,從而實現沙箱機制。

通(tong)常我們是(shi)將副(fu)本(ben)用(yong)于操作,操作結束時,將副(fu)本(ben)的(de)更(geng)新寫入原始(shi)運(yun)行環境,舉個例(li)子:

const snapshotSandbox = {
  original: null,
  copied: null,
  beforeAction: (obj, dangerKeys) => {
    // 保留原始副本
    this.original = obj;
    const snapshot = {};
    // 記錄當前信息,做一次快照
    for (const key of Object.keys(obj)) {
      if (dangerKeys.includes(key)) {
        // 對于敏感信息,不提供或提供加密信息
        continue;
      }
      snapshot[key] = obj[key];
    }
    console.log(snapshot);
    this.copied = snapshot;
    return this.copied;
  },
  afterAction: () => {
    // 將操作結果更新到原始副本中
    for (const key of Object.keys(this.copied)) {
      this.original[key] = this.copied[key];
    }
  }
}
 

function getUserId(user) {
  const id = user.id;
  const name = user.name;
  console.log('id', id, 'name', name); 
  // ajax('用于第三方不安全平臺登記認證');
}
const code = getUserId.toString();

const base = {
  id: 'xxxx',
  name: '一個人',
  age: '12',
  phoneNo: '1333333333333'
};

// 1. 現在假設有一個危險操作被注入到頁面上會使用用戶的 ID 信息(身份證)
const userInfo = snapshotSandbox.beforeAction(base, ['id', 'phoneNo']);
// 2. 做壞事
;
// 3. 把更新的內容寫回原來的對象
snapshotSandbox.afterAction();
console.log('使用的 userInfo 信息', userInfo);
console.log('最終操作后的 userInfo 信息', base);
console.log('這兩個信息理論上不是同一個對象', userInfo !== base)

這個實現僅僅是(shi)(shi)其(qi)中(zhong)一種方(fang)(fang)式,在微前(qian)端中(zhong),基于快照的(de)實現在以(yi)前(qian)也是(shi)(shi)一種流行的(de)版本,目的(de)是(shi)(shi)為(wei)了隔離不同子(zi)應(ying)用,現在多數基于 Proxy 來實現了,但(dan)快照沙箱依舊是(shi)(shi)作為(wei)一種降級(ji)方(fang)(fang)案去兼容老舊的(de)瀏覽器。

最后

我們從應用場景的角度分析,JS 沙箱聚焦于 “安全防護” 與 “應用隔離” 這兩大核心需求:
在 Web 瀏覽器端,JS 沙箱能夠限制用戶自行編寫的代碼作用范圍。例如,它可實現攔截彈窗廣告、抵御 XSS 攻擊等功能,避免不可信的 JS 文件對頁面正常運行造成干擾。而在服務器端,如 LeetCode 代碼提交、在線編輯器等,沙箱可以提供有力保障。它能夠防止用戶提交的惡意代碼突破權限限制,從而避免服務器配置被篡改以及數據被竊取的風險。
盡管 Web 瀏(liu)覽器(qi)端(duan)(duan)和(he)服務(wu)器(qi)端(duan)(duan)的(de)部署環境存在差異,然而它們的(de)最終目(mu)標是一致的(de):即,確保代碼(ma)在可控范圍內運行(xing),降低(di)風險擴散的(de)可能(neng)性(xing)。

參考文章

[1]:

[2]:

[3]:

posted @ 2025-09-25 13:55  Achieve前端實驗室  閱讀(113)  評論(0)    收藏  舉報