WebWorkerはJavaScriptサンドボックスの詳細をカプセル化します

WebWorkerはJavaScriptサンドボックスの詳細をカプセル化します

1. シナリオ

前回の記事では、Quickjs が JavaScript サンドボックスの詳細をカプセル化し、 quickjsに基づいてサンドボックスが実装されました。ここでは、Web ワーカーに基づいて代替ソリューションが実装されています。 web worker何であるか分からない場合、または一度も調べたことがない場合は、 Web Workers API確認してください。つまり、これはブラウザに実装されたマルチスレッドであり、別のスレッドでコードを実行し、そのコードと通信する機能を提供します。

2. IJavaScriptShadowboxを実装する

実際、Web Worker はpostMessage/onmessageというevent emitter API を提供しているため、実装は非常に簡単です。

実装は 2 つの部分に分かれており、1 つはメイン スレッドでIJavaScriptShadowboxを実装すること、もう 1 つはweb workerスレッドでIEventEmitter実装することです。

2.1 メインスレッドの実装

「./IJavaScriptShadowbox」から {IJavaScriptShadowbox} をインポートします。

エクスポートクラス WebWorkerShadowbox は IJavaScriptShadowbox を実装します {
  破棄(): void {
    this.worker.terminate();
  }

  民間労働者!:労働者;
  eval(コード: 文字列): void {
    const blob = new Blob([code], { type: "application/javascript" });
    this.worker = 新しいWorker(URL.createObjectURL(blob), {
      資格情報: "include",
    });
    this.worker.addEventListener("メッセージ", (ev) => {
      const msg = ev.data as { チャネル: 文字列; データ: 任意 };
      // console.log('msg.data: ', msg)
      if (!this.listenerMap.has(msg.channel)) {
        戻る;
      }
      this.listenerMap.get(msg.channel)!.forEach((handle) => {
        ハンドル(msg.data);
      });
    });
  }

  プライベート読み取り専用リスナーマップ = 新しい Map<string, ((data: any) => void)[]>();
  出力(チャンネル: 文字列、データ: 任意): void {
    this.worker.postMessage({
      チャンネル: チャンネル、
      データ、
    });
  }
  on(チャンネル: 文字列、ハンドル: (データ: 任意) => void): void {
    if (!this.listenerMap.has(channel)) {
      this.listenerMap.set(チャンネル、[]);
    }
    this.listenerMap.get(チャンネル)!.push(ハンドル);
  }
  offByChannel(チャンネル: 文字列): void {
    this.listenerMap.delete(チャンネル);
  }
}

2.2 Webワーカースレッドの実装

「./IEventEmitter」から IEventEmitter をインポートします。

エクスポートクラスWebWorkerEventEmitterはIEventEmitterを実装します{
  プライベート読み取り専用リスナーマップ = 新しい Map<string, ((data: any) => void)[]>();

  出力(チャンネル: 文字列、データ: 任意): void {
    postMessage({
      チャンネル: チャンネル、
      データ、
    });
  }

  on(チャンネル: 文字列、ハンドル: (データ: 任意) => void): void {
    if (!this.listenerMap.has(channel)) {
      this.listenerMap.set(チャンネル、[]);
    }
    this.listenerMap.get(チャンネル)!.push(ハンドル);
  }

  offByChannel(チャンネル: 文字列): void {
    this.listenerMap.delete(チャンネル);
  }

  初期化() {
    onmessage = (ev) => {
      const msg = ev.data as { チャネル: 文字列; データ: 任意 };
      if (!this.listenerMap.has(msg.channel)) {
        戻る;
      }
      this.listenerMap.get(msg.channel)!.forEach((handle) => {
        ハンドル(msg.data);
      });
    };
  }

  破壊する() {
    このリスナーマップをクリアします。
    onmessage = null;
  }
}

3. WebWorkerShadowbox/WebWorkerEventEmitterを使用する

メインスレッドコード

const シャドウボックス: IJavaScriptShadowbox = new WebWorkerShadowbox();
shadowbox.on("hello", (名前: 文字列) => {
  console.log(`hello ${name}`);
});
// ここでのコードは、shadowbox.eval(code); の下の Web ワーカー スレッドのコードを参照します。
shadowbox.emit("open");


Web ワーカー スレッド コード

const em = 新しい WebWorkerEventEmitter();
em.on("open", () => em.emit("hello", "liuli"));


以下はコード実行フローの概略図です。Web web workerサンドボックス実装では、サンプル コード実行フローが使用されます。

4. WebワーカーのグローバルAPIを制限する

JackWoeker指摘したように、 web workerは安全でないAPIが多数あるため、以下のAPIを含むがこれらに限定されないAPIを制限する必要がある。

  • fetch
  • indexedDB
  • performance

実際、 web workerにはデフォルトで276グローバル API が付属しており、これは私たちが考えているよりもはるかに多い可能性があります。

performance/SharedArrayBuffer apiを介して Web 上でサイドチャネル攻撃を実行する方法を説明した記事があります。SharedArrayBuffer在SharedArrayBuffer api現在ブラウザーでデフォルトで無効になっていますが、他の方法があるかどうかは誰にもわかりません。したがって、最も安全な方法は、API ホワイトリストを設定してから、ホワイトリストに登録されていない API を削除することです。

// ホワイトリストWorkerGlobalScope.ts
/**
 * Webワーカーランタイムのホワイトリストを設定して、安全でないAPIをすべて禁止する
 */
エクスポート関数 whitelistWorkerGlobalScope(list: PropertyKey[]) {
  const ホワイトリスト = 新しい Set(リスト);
  const all = Reflect.ownKeys(globalThis);
  すべて.forEach((k) => {
    if (ホワイトリスト.has(k)) {
      戻る;
    }
    if (k === "ウィンドウ") {
      console.log("ウィンドウ: ", k);
    }
    Reflect.deleteProperty(globalThis, k);
  });
}

/**
 * グローバル値のホワイトリスト */
定数ホワイトリスト: (
  | キーof タイプof グローバル
  | WindowOrWorkerGlobalScope のキー
  | 「コンソール」
)[] = [
  "グローバルこれ",
  "コンソール"、
  "タイムアウトの設定",
  「タイムアウトをクリア」、
  "setInterval"、
  「クリア間隔」、
  「ポストメッセージ」、
  "オンメッセージ",
  "反映する"、
  "配列"、
  "地図"、
  "セット"、
  "関数"、
  "物体"、
  「ブール値」、
  "弦"、
  "番号"、
  "数学"、
  "日付"、
  「JSON」、
];

ホワイトリストWorkerGlobalScope(ホワイトリスト);

次に、サードパーティのコードを実行する前に上記のコードを実行します。

「./whitelistWorkerGlobalScope.js?raw」からbeforeCodeをインポートします。

エクスポートクラス WebWorkerShadowbox は IJavaScriptShadowbox を実装します {
  破棄(): void {
    this.worker.terminate();
  }

  民間労働者!:労働者;
  eval(コード: 文字列): void {
    // この行がキーです const blob = new Blob([beforeCode + "\n" + code], {
      タイプ: "application/javascript",
    });
    // その他のコード。 。 。
  }
}

ソース コードの記述には ts を使用するため、 ts をjs bundleにパッケージ化し、それをviteの ? rawを通じて文字列としてインポートする必要もあります。以下では、これを行うための簡単なプラグインを作成しました。

「vite」からdefineConfigとPluginをインポートします。
「@vitejs/plugin-react-refresh」から reactRefresh をインポートします。
「vite-plugin-checker」からチェッカーをインポートします。
「esbuild」から{build}をインポートします。
"path" から * をパスとしてインポートします。

エクスポート関数buildScript(scriptList: string[]): プラグイン{
  _scriptList を scriptList.map((src) => path.resolve(src));
  非同期関数buildScript(src: 文字列) {
    ビルドを待つ({
      エントリポイント: [src],
      出力ファイル: src.slice(0, src.length - 2) + "js",
      フォーマット: "iife",
      バンドル: true、
      プラットフォーム:「ブラウザ」、
      ソースマップ: "インライン",
      上書きを許可する: true、
    });
    console.log("ビルドが完了しました: ", path.relative(path.resolve(), src));
  }
  戻る {
    名前: "vite-plugin-build-script",

    非同期configureServer(サーバー) {
      server.watcher.add(_scriptList);
      _scriptList を新しい Set に追加します。
      server.watcher.on("change", (filePath) => {
        // console.log('変更: ', ファイルパス)
        スクリプトセットにファイルパスがある場合
          ビルドスクリプト(ファイルパス);
        }
      });
    },
    非同期ビルド開始() {
      // console.log('buildStart: ', this.meta.watchMode)
      if (this.meta.watchMode) {
        _scriptList.forEach((src) => this.addWatchFile(src));
      }
      Promise.all(_scriptList.map(buildScript)) を待機します。
    },
  };
}

// https://vitejs.dev/config/
デフォルトのdefineConfigをエクスポートする({
  プラグイン: [
    反応リフレッシュ()、
    チェッカー({typescript: true})、
    ビルドスクリプト([path.resolve("src/utils/app/whitelistWorkerGlobalScope.ts")]),
  ]、
});

これで、 web worker内のグローバル API はホワイトリストにあるものだけであることがわかります。

5. Webワーカーサンドボックスの主な利点

chrome devtoolを使用して直接デバッグし、 console/setTimeout/setInterval api
をサポートできます。 console/setTimeout/setInterval api
メッセージ通信を直接サポートするapi

WebWorker カプセル化 JavaScript サンドボックスの詳細に関するこの記事はこれで終わりです。WebWorker カプセル化 JavaScript サンドボックスに関する関連コンテンツの詳細については、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • Quickjs は JavaScript サンドボックスの詳細をカプセル化します
  • JavaScript サンドボックスの探索
  • JavaScript Sandboxについての簡単な説明
  • フロントエンドJSサンドボックスを実装するいくつかの方法についての簡単な説明
  • Node.jsサンドボックス環境についての簡単な説明
  • Node.js アプリケーション用の安全なサンドボックス環境の設定
  • JS実装クロージャにおけるサンドボックスモードの例
  • JS サンドボックス モードの例の分析
  • JavaScript デザインパターン セキュリティ サンドボックス モード

<<:  Docker の Windows ストレージ パス設定操作

>>:  10分でCSS3グリッドレイアウトを理解する

推薦する

Win10 DVWA のダウンロード、インストール、構成のグラフィック チュートリアルの詳細な説明 (初心者向け学習侵入)

コンピュータ システムが再インストールされ、侵入テスト学習環境 DVWA を再インストールする必要が...

HTML コード内のスペースと空白行についての簡単な説明

HTML コード内の連続するスペースまたは空白行 (改行) はすべて 1 つのスペースとして表示され...

MySQLデータベースの管理者パスワードを忘れた場合の解決策

1. コマンド mysqld --skip-grant-tables を入力します (前提条件: m...

HTMLシールドの右クリックメニューと左クリック入力機能の例

右クリックメニューを無効にする <body oncontextmenu=self.event....

td セルを結合した場合の td 幅の問題

以下の例では、名前が入っている td の幅が 60px のとき、2 行目の文字数が少ない場合は正常に...

MySQL でのインデックスの追加と削除に関連する操作

目次1. インデックスの役割2. インデックスの作成と削除(1)ALTER TABLE文を使用して、...

フォーム内の無効なフォームフィールドの値を送信する方法 サンプルコード

フォーム内のフォーム フィールドが無効に設定されている場合、フォーム フィールドの値は送信されません...

Linux でソースインストールされたパッケージを簡単に削除する方法

ステップ1: Stowをインストールするこの例では CentOS を使用しているため、拡張 EPEL...

CSS を使用して複数の方法で等幅レイアウトを実装するサンプルコード

この記事で説明する等幅レイアウトでは、純粋な CSS を使用して、要素の幅を手動で設定することなく、...

ウェブフロントエンドに対する一般的な攻撃とその防止方法

ウェブサイトのフロントエンド開発で発生するセキュリティ問題は、クライアントブラウザで実行されるコード...

Linux ファイル管理コマンド例の分析 [表示、閲覧、統計など]

この記事では、Linux ファイル管理コマンドについて例を挙げて説明します。ご参考までに、詳細は以下...

ろうそくを溶かす(水滴)サンプルコードを実現する純粋な CSS

成果を達成する実装のアイデアフィルターのコントラストとぼかしを利用して溶ける効果を実現します。親要素...

Reactイベントメカニズムソースコード分析

目次原理ソースコード分析委任されたイベントバインディングすべてのサポートされているイベントを聴くネイ...

MySQL データベース ターミナル - 一般的な操作コマンド コード

目次1. ユーザーを追加する2. ユーザー名とホストを変更する3. パスワードを変更する4. ユーザ...

IE で ClearType をオンにした後の透明フォントの問題の解決方法

IE で ClearType をオンにした後に発生する透明フォントの問題を解決するには、透明要素に背...