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グリッドレイアウトを理解する

推薦する

MySQLクエリの基本的なクエリ操作の学習

序文MySQL は最も人気のあるリレーショナル データベース管理システムです。WEB アプリケーショ...

体験をデザインする: ボタンには何があるか

<br />最近、UCDChina は「インターフェース上のテキストに注意を払う」という...

Windows 10 と MySQL 5.5 のインストールとインストールなしの使用の詳細なチュートリアル (画像とテキスト)

この記事では、Windows 10環境でのMySQL 5.5のインストールと使用方法を紹介します。リ...

Vueタブとキャッシュページを切り替えるいくつかの方法

目次1. 切り替え方法2. タブを動的に生成する3. キャッシュコンポーネント3.1 キープアライブ...

Centos で MySQL パスワードを変更する方法

1. MySQL ログイン設定を変更します。 # vim /etc/my.cnf文を追加: skip...

Vue は QR コード スキャン機能を実装します (スタイル付き)

必要: vue を使用して QR コードのスキャンを実現します。プラグイン: QRコードリーダー;プ...

ソースコードから、Vue2がデータとメソッドを直接取得できる理由がわかる

目次1. 例: これはデータとメソッドを直接取得できます2. 環境を準備し、ソースコードをデバッグし...

MySQL 起動エラー 1067 および文字セットを変更して再起動した後の無効な回復

公式サイトからmysql-5.6.37-winx64.zipの解凍バージョンをダウンロードし、構成フ...

MySQL 8.0.19 winx64 インストールチュートリアルと Windows 10 での初期パスワードの変更

この記事では、参考までにMySQL 8.0.19 winx64のインストールチュートリアルを紹介しま...

vueの実践的な応用におけるvuexの永続性の詳細な説明

目次vuex 永続性要約するvuex 永続性vuex: ブラウザを更新すると、vuexの状態は初期状...

Node.js コード実行をバイパスするためのヒントのまとめ

目次1. 子プロセス2. nodejsでのコマンド実行2.1 16進数エンコード2.2 ユニコードエ...

MyBatisインターセプターのページング機能を実装する方法

MyBatisインターセプターのページング機能を実装する方法序文:まず、実装原則についてお話しします...

nginx ssl を設定して https アクセスを実装する手順 (初心者向け)

序文サーバーを展開した後、私は大きな喜びを感じながら自分の Web サイトにアクセスし、見たものすべ...

Ubuntu 18.04 で中国語入力方法を設定する方法

Ubuntuの最新バージョンでは、ユーザーは中国語入力方法を別途ダウンロードする必要がなくなりました...

【Webデザイン】E-WebTemplates の美しい海外の Web ページ テンプレート (FLASH+PSD ソース ファイル+HTML) を共有します

これらはすべて海外のE-WebTemplates WebサイトからのWebページテンプレートであり、...