JavaScriptサンドボックスの深掘り

[![表紙](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/5262564c-1c7a-0d9c-23d3-e66c23b45976.png)](https://leapcell.io/?lc_t=q_jssandbox)

JavaScript におけるサンドボックス（sandbox）とは、安全機構の一種であり、コードの実行を分離し、アプリケーションやシステムの他の部分に不要な影響やセキュリティリスクを与えないようにする仕組みです。サンドボックスは、制御された環境を提供し、その環境内でコードを実行できるようにすることで、外部環境に影響を与えずにユーザーデータやシステムの安全を保護します。

ブラウザにおいて、サンドボックスとは通常、各タブごとに提供される隔離環境を指します。この隔離により、あるタブ内の JavaScript コードは、別のタブの内容にアクセスできません。ただし、同一オリジンポリシー（プロトコル、ドメイン、ポートが同一であること）を満たすか、CORS（クロスオリジンリソース共有）を通じて明示的に許可されている場合には、クロスオリジンアクセスが可能になります。また、Web ページのコードは IPC（プロセス間通信）を通じてブラウザのコアプロセスと通信し、その通信はセキュリティチェックを受けます。サンドボックスの設計目的は、信頼できないコードを特定の環境内で実行させ、隔離された領域外のリソースへのアクセスを制限することです。

## JavaScript のサンドボックスの使用シナリオ

JavaScript において、サンドボックスは通常、コードの実行環境を分離・制御し、安全で制限された環境内でコードを動作させるために使用されます。これにより、メイン環境に対する潜在的な被害を防ぐことができます。

- **サードパーティの JavaScript の実行**：信頼できないサードパーティの JavaScript コードを実行する必要がある場合に、サンドボックスを使用して安全に実行できます。
- **オンラインコードエディター**：多くのオンラインコードエディターでは、ユーザーが記述したコードをサンドボックス内で実行し、ページ全体に影響を及ぼさないようにしています。
- **Web アプリケーションのセキュリティ**：異なるオリジンから取得した JavaScript コードをブラウザ上で実行する際、サンドボックスを使用することで、権限を制限し、悪意のあるコードが機密情報へアクセスしたり危険な操作を実行するのを防ぎます。
- **プラグインやサードパーティスクリプト**：Web アプリケーションが外部のプラグインやスクリプトをロードして実行する際、サンドボックスを用いることで、それらのスクリプトのアクセス権限を制限し、メインアプリケーションの安全性とデータを保護します。
- **JSONP の解析**：サーバーから返される JSONP データが信頼できない場合、サンドボックスを使用してデータを解析し、安全に取得することができます。

## JSONP 通信の仕組み

JSONP（JSON with Padding）の原理は、`<script>` タグがクロスオリジン制限を受けないという特性を利用して、サードパーティと通信を行うことにあります。通信が必要な場合、スクリプトは `<script>` タグを作成し、その `src` 属性にサードパーティ API の URL を設定します。例えば：

```html
<script src="http://www.example.net/api?param1=1&param2=2"></script>
```

また、サーバー側の応答は JSON データを特定の関数（callback 関数）にラップして返します（これが「JSON Padding」の由来です）。例えば：

```js
callback({ name: 'hax', gender: 'Male' });
```

これにより、ブラウザは `callback` 関数を実行し、解析された JSON オブジェクトを引数として渡します。

### サンドボックスを利用した JSONP 解析の基本手順

1. **独立した iframe を作成**：メインページ内に動的に iframe を作成し、JSONP リクエストを実行するための隔離環境を提供します。この iframe はメインページの DOM へアクセスできないため、悪意のあるコードの影響を制限できます。
2. **iframe 内で JSONP リクエストを発行**：iframe に `<script>` タグを挿入し、JSONP リクエストを実行させます。これにより、悪意のあるスクリプトがあった場合でも、その影響は iframe 内部に限定されます。
3. **安全なデータ取得**：iframe 内のスクリプトは、`postMessage` API などを用いて、安全にメインページへデータを送信できます。メインページはデータの出所を検証し、セキュリティ対策を施したうえでデータを利用します。
4. **iframe の動作を制限・監視**：CSP（Content Security Policy）を活用して、iframe 内でロード・実行できるリソースを制限することで、さらなるセキュリティ強化を図ることができます。

この方法により、JSONP の応答データに不審なコードが含まれていても、その影響を完全に隔離し、ユーザーデータとシステムの安全性を確保できます。

## `with` + `new Function` を利用したサンドボックスの実装

JavaScript では、`with` 文と `new Function` を使用して、簡単なサンドボックス環境を作成することができます。この方法では、コードの実行スコープを制限し、グローバル変数へのアクセスや安全でない操作を防ぐことができます。

`with` 文のブロック内では、変数のアクセスが渡されたオブジェクトのプロパティを優先的に参照するため、コード内の変数アクセスを制御することが可能になります。

```js
function createSandbox(code) {
  // 空のオブジェクトを作成し、サンドボックスのグローバルオブジェクトとして使用
  const sandbox = {};

  // with 文を使ってコードのスコープを sandbox に制限
  // new Function を使用して、新しい関数を作成し、外部スコープへのアクセスを制限
  const script = new Function('sandbox', `with(sandbox) { ${code} }`);

  // 関数を実行し、sandbox を渡す
  return function () {
    script(sandbox);
  };
}

// サンドボックス環境でコードを実行
const sandboxedScript = createSandbox('console.log("Hello from the sandbox!"); var x = 10;');
sandboxedScript(); // 出力: Hello from the sandbox!
console.log(typeof x); // 出力: undefined（x はサンドボックス内で定義されており、外部からアクセスできない）
```

この `createSandbox` 関数は、コード文字列を引数として受け取り、そのコードをサンドボックス内で実行する環境を作成します。`sandbox` という空のオブジェクトを定義し、`with(sandbox)` によってスコープを制限することで、コードの変数や関数定義が `sandbox` 内に閉じ込められます。

ただし、この方法にはいくつかのセキュリティ上の問題があります。特に `new Function` を使用することで、サンドボックス内のコードが `Function` コンストラクタにアクセスできる場合、制限を回避して任意のコードを実行する可能性があります。そのため、`new Function` + `with` を用いたサンドボックスは「善意のユーザー向け」には有効ですが、悪意のあるコードを完全に防ぐものではありません。

## `iframe` を利用したサンドボックス

`iframe` を利用してサンドボックス環境を作成する方法は、Web 開発において一般的な手法です。この方法では、現在のページ内に独立した HTML ページを埋め込むことで、JavaScript の実行環境を完全に分離し、スクリプトがメインページの DOM や JavaScript 環境にアクセスできないようにします。

### `iframe` を用いたサンドボックスの基本構造

まず、HTML に `iframe` を配置します。

```html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>サンドボックス</title>
  </head>
  <body>
    <iframe id="sandbox" style="display: none"></iframe>
    <script src="index.js"></script>
  </body>
</html>
```

次に、`index.js` で `iframe` の `contentWindow` を操作し、サンドボックス環境を実現します。

```js
// index.js
function createSandbox(callback) {
  const iframe = document.getElementById('sandbox');
  if (!iframe) {
    return console.error('サンドボックス用のiframeが見つかりません');
  }

  // iframeが完全にロードされた後にコードを実行
  iframe.onload = function () {
    const iframeWindow = iframe.contentWindow;

    // サンドボックス内で使用可能な変数や関数を定義
    iframeWindow.safeGlobalVar = {
      /* 安全なデータやメソッド */
    };

    // コールバック関数を実行し、iframeのwindowオブジェクトを渡す
    callback(iframeWindow);
  };

  // 環境をクリアするためにiframeをリロード
  iframe.src = 'about:blank';
}

// サンドボックスを利用
createSandbox(function (sandboxWindow) {
  // サンドボックス環境内でコードを実行
  sandboxWindow.eval('console.log("Hello from the sandbox!");');
});
```

### `sandbox` 属性による制限

HTML5 では、`iframe` に `sandbox` 属性を指定することで、より安全に環境を制限することができます。

```html
<iframe src="sandbox.html" sandbox="allow-scripts" id="sandbox"></iframe>
```

`sandbox` 属性には、以下のような制限をかけるオプションがあります。

- `allow-scripts`：スクリプトの実行を許可
- `allow-same-origin`：同一オリジンのページとの通信を許可
- `allow-forms`：フォーム送信を許可
- `allow-popups`：`window.open` などのポップアップを許可
- `allow-top-navigation`：親フレームへのナビゲーションを許可

この機能を利用すれば、さらに安全に `iframe` を用いたサンドボックス環境を構築できます。

## Web Workers を利用したサンドボックス

Web Workers を用いたサンドボックスの実装では、動的に `Blob` オブジェクトを作成し、その中に実行する JavaScript コードを含めます。この `Blob` を `Worker` に渡すことで、メインスレッドとは隔離された環境でコードを実行できます。

```js
function workerSandbox(appCode) {
  var blob = new Blob([appCode]);
  var appWorker = new Worker(window.URL.createObjectURL(blob));
}

workerSandbox('const a = 1;console.log(a);'); // 出力: 1
console.log(typeof a); // 出力: undefined（aはメインスレッドで定義されていない）
```

Web Workers を利用したサンドボックスは、Web アプリケーション内でコードを隔離して実行する有効な手段です。特に、UI の応答性を保ちつつ、複雑な処理や潜在的なリスクを伴うコードを実行する場面で有効です。

## まとめ

JavaScript のサンドボックス環境は、実行コードを分離し、アプリケーションの状態やデータを保護するための重要な仕組みです。サンドボックスを利用することで、信頼できないコードの実行を制限し、グローバル変数や関数へのアクセスを防ぎながら、セキュリティリスクやデータ漏洩の危険性を最小限に抑えることができます。

サンドボックスの実装方法としては、`with` + `new Function`、`iframe`、`Web Workers` などがあり、それぞれの用途やリスクに応じて適切な手法を選択することが重要です。Web 開発において、信頼できないスクリプトや外部のコードを実行する際には、サンドボックスの活用が不可欠となります。

---

### [私たちはLeapcell、Node.jsプロジェクトのホスティングの最適解です。](https://leapcell.io/?lc_t=q_jssandbox)

[![Leapcell](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/dc8658bb-9311-96c4-7535-f086591d66b8.png)](https://leapcell.io/?lc_t=q_jssandbox)

[Leapcell](https://leapcell.io/?lc_t=q_jssandbox)は、Webホスティング、非同期タスク、Redis向けの次世代サーバーレスプラットフォームです：

**複数言語サポート**

- Node.js、Python、Go、Rustで開発できます。

**無制限のプロジェクトデプロイ**

- 使用量に応じて料金を支払い、リクエストがなければ料金は発生しません。

**比類のないコスト効率**

- 使用量に応じた支払い、アイドル時間は課金されません。
- 例: $25で6.94Mリクエスト、平均応答時間60ms。

**洗練された開発者体験**

- 直感的なUIで簡単に設定できます。
- 完全自動化されたCI/CDパイプラインとGitOps統合。
- 実行可能なインサイトのためのリアルタイムのメトリクスとログ。

**簡単なスケーラビリティと高パフォーマンス**

- 高い同時実行性を容易に処理するためのオートスケーリング。
- ゼロ運用オーバーヘッド — 構築に集中できます。

[ドキュメントで詳細を確認！](https://docs.leapcell.io/)

[![Try Leapcell](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/49cd3680-6f90-954b-3e94-9305b94916af.png)](https://leapcell.io/?lc_t=q_jssandbox)

Xでフォローする：[@LeapcellHQ](https://x.com/LeapcellHQ)

---

[ブログでこの記事を読む](https://leapcell.io/blog/deep-dive-into-javascript-sandboxing)