ホームページ ウェブフロントエンド jsチュートリアル アプリケーションサンドボックス環境の活用

アプリケーションサンドボックス環境の活用

Jun 06, 2018 pm 02:48 PM
node.js

今回はアプリケーションサンドボックス環境の実際の使用方法についてお届けします。実際にアプリケーションサンドボックス環境を使用する際の注意点は何ですか?

スクリプトを動的に実行するためのシナリオは何ですか?

一部のアプリケーションでは、Microsoft Office の VBA、一部のゲームの lua スクリプト、ユーザーがカスタム ロジックを投稿できる FireFox の「Grease Monkey Script」などのカスタム ロジックを挿入できる機能をユーザーに提供したいと考えています。想像力を駆使して、自分の制御範囲と権限の範囲内で楽しくて役立つことを実行し、ユーザーの個別のニーズを満たすために機能を拡張します。

それらのほとんどはクライアント プログラムであり、一部のオンライン システムや製品では同様の要件が存在することがよくあります。実際、多くのオンライン アプリケーションでは、Google ドキュメント スクリプトのアプリなど、スクリプトをカスタマイズする機能も提供されており、非常に便利な機能を実行できます。 JavaScript を使用した作業 (ドキュメントを開くイベントやセル変更イベントに応じたコードの実行、数式用のカスタム スプレッドシート関数の作成など)。

「ユーザーのコンピュータ上で」実行されているクライアント アプリケーションとは異なり、ユーザーのカスタム スクリプトは通常、ユーザー自身にのみ影響します。オンライン アプリケーションやサービスの場合、「セキュリティ」、ユーザーの「カスタム スクリプト」など、いくつかの状況がより重要になります。 " は厳密に制限され、隔離されている必要があります。つまり、ホスト プログラムや他のユーザーに影響を与えることはできません。

Safeify は、ユーザー定義の信頼できないスクリプトを安全に実行するために使用される Nodejs アプリケーションのモジュールです。

動的スクリプトを安全に実行するにはどうすればよいですか?

まず、JavaScript プログラムでコードの一部を動的に実行する方法を見てみましょう。例えば、有名な eval

eval('1+2')

上記のコードは問題なくスムーズに実行されました。 eval はグローバルオブジェクトの関数属性であり、実行されたコードは他のコードと同じプロパティを持ちます。アプリケーションプロセス内の通常のコードでは、許可があれば「実行コンテキスト」のローカル変数とすべての「グローバル変数」にアクセスできますが、このシナリオでは非常に危険な関数です。

Functionn をもう一度見てみましょう。Function コンストラクターを使用して関数を動的に作成して実行できます

const sum = new Function('m', 'n', 'return m + n');
console.log(sum(1, 2));
ログイン後にコピー

また、Function コンストラクターを使用して生成された関数は、そのコンテキストを作成しません。クロージャはグローバル スコープで作成されます。 。関数を実行するときは、独自のローカル変数とグローバル変数にのみアクセスできますが、呼び出される Function コンストラクターによって生成されたコンテキストのスコープにはアクセスできません。地面に立つ者と薄い紙の上に立つ者のように、この場面では優劣の区別がほとんどない。

ES6 の新機能プロキシと組み合わせると、より安全になります

function evalute(code,sandbox) {
 sandbox = sandbox || Object.create(null);
 const fn = new Function('sandbox', `with(sandbox){return ($[code])}`);
 const proxy = new Proxy(sandbox, {
 has(target, key) {
  // 让动态执行的代码认为属性已存在
  return true; 
 }
 });
 return fn(proxy);
}
evalute('1+2') // 3
evalute('console.log(1)') // Cannot read property 'log' of undefined
ログイン後にコピー

eval であっても関数であっても、スコープは実行中にレイヤーごとに検索され、見つからない場合はグローバルまで移動することがわかっています。したがって、プロキシの原理を使用します。つまり、実行されたコードは、「エスケープ防止」の目的を達成するためにsandobx内で見つけることができます。

ブラウザーでは、iframe を使用して再送信に対して安全な分離環境を作成することもできます。この記事では Node.js に重点を置いているため、ここではあまり説明しません。
Node.js には他にオプションはありますか?

おそらく、これを見る前に VM についてすでに考えたことがあるでしょう。これは、Node.js によってデフォルトで提供される組み込みモジュールであり、V8 仮想マシン環境でコードをコンパイルおよび実行するための一連の API を提供します。 JavaScript コードは、コンパイルしてすぐに実行することも、コンパイルして保存してから実行することもできます。

const vm = require('vm');
const script = new vm.Script('m + n');
const sandbox = { m: 1, n: 2 };
const context = new vm.createContext(sandbox);
script.runInContext(context);
ログイン後にコピー

上記のコードを実行して結果3を取得します。同時に、vm.Scriptを通じてコード実行の「最大ミリ秒数」を指定することもでき、指定した時間を超えると実行が停止されます。

try {
 const script = new vm.Script('while(true){}',{ timeout: 50 });
 ....
} catch (err){
 //打印超时的 log
 console.log(err.message);
}
ログイン後にコピー

上記のスクリプトの実行は失敗し、タイムアウトが検出され、例外がスローされ、Try Cache によってキャプチャされ、ログに記録されることに注意してください。 vm.Script のは「同期生成に対してのみ有効」であり、非同期は含まれません。上記のコード

const script = new vm.Script('setTimeout(()=>{},2000)',{ timeout: 50 });
 ....
ログイン後にコピー

などの呼び出し時間は、50 ミリ秒を超えるコードの同期実行が完了する必要があるため、50 ミリ秒後に例外をスローしません。 setTimeout で使用される時間は含まれていません。つまり、vm モジュールには非同期コードで実行時間を直接制限する方法がありません。また、タイムアウトをチェックするために追加のタイマーを使用することもできません。これは、チェック後に実行中の VM を中止する方法がないためです。

さらに、Node.js では、vm.runInContext はコード実行環境を分離しているように見えますが、実際には「エスケープ」するのは簡単です。

const vm = require('vm');
const sandbox = {};
const script = new vm.Script('this.constructor.constructor("return process")().exit()');
const context = vm.createContext(sandbox);
script.runInContext(context);
ログイン後にコピー

执行上边的代码,宿主程序立即就会「退出」,sandbox 是在 VM 之外的环境创建的,需 VM 中的代码的 this 指向的也是 sandbox,那么

//this.constructor 就是外所的 Object 构建函数
const ObjConstructor = this.constructor; 
//ObjConstructor 的 constructor 就是外包的 Function
const Function = ObjConstructor.constructor;
//创建一个函数,并执行它,返回全局 process 全局对象
const process = (new Function('return process'))(); 
//退出当前进程
process.exit();
ログイン後にコピー

没有人愿意用户一段脚本就能让应用挂掉吧。除了退出进程序之外,实际上还能干更多的事情。

有个简单的方法就能避免通过 this.constructor 拿到 process,如下:

const vm = require('vm');
//创建一外无 proto 的空白对象作为 sandbox
const sandbox = Object.create(null);
const script = new vm.Script('...');
const context = vm.createContext(sandbox);
script.runInContext(context);
ログイン後にコピー

但还是有风险的,由于 JavaScript 本身的动态的特点,各种黑魔法防不胜防。事实 Node.js 的官方文档中也提到 VM 当做一个安全的沙箱去执行任意非信任的代码。

有哪些做了进一步工作的社区模块?

在社区中有一些开源的模块用于运行不信任代码,例如 sandbox、vm2、jailed 等。相比较而言 vm2 对各方面做了更多的安全工作,相对安全些。

从 vm2 的官方 READM 中可以看到,它基于 Node.js 内建的 VM 模块,来建立基础的沙箱环境,然后同时使用上了文介绍过的 ES6 的 Proxy 技术来防止沙箱脚本逃逸。

用同样的测试代码来试试 vm2

const { VM } = require('vm2');
new VM().run('this.constructor.constructor("return process")().exit()');
ログイン後にコピー

如上代码,并没有成功结束掉宿主程序,vm2 官方 REAME 中说「vm2 是一个沙盒,可以在 Node.js 中按全的执行不受信任的代码」。

然而,事实上我们还是可以干一些「坏」事情,比如:

const { VM } = require('vm2');
const vm = new VM({ timeout: 1000, sandbox: {}});
vm.run('new Promise(()=>{})');
ログイン後にコピー

上边的代码将永远不会执行结束,如同 Node.js 内建模块一样 vm2 的 timeout 对异步操作是无效的。同时,vm2 也不能额外通过一个 timer 去检查超时,因为它也没有办法将执行中的 vm 终止掉。这会一点点耗费完服务器的资源,让你的应用挂掉。

那么或许你会想,我们能不能在上边的 sandbox 中放一个假的 Promise 从而禁掉 Promise 呢?答案是能提供一个「假」的 Promise,但却没有办法完成禁掉 Promise,比如

const { VM } = require('vm2');
const vm = new VM({ 
 timeout: 1000, sandbox: { Promise: function(){}}
});
vm.run('Promise = (async function(){})().constructor;new Promise(()=>{});');
ログイン後にコピー

可以看到通过一行 Promise = (async function(){})().constructor 就可以轻松再次拿到 Promise 了。从另一个层面来看,况且或许有时我们还想让自定义脚本支持异步处理呢。

如何建立一个更安全一些的沙箱?

通过上文的探究,我们并没有找到一个完美的方案在 Node.js 建立安全的隔离的沙箱。其中 vm2 做了不少处理,相对来讲算是较安全的方案了,但问题也很明显,比如异步不能检查超时的问题、和宿主程序在相同进程的问题。

没有进程隔离时,通过 VM 创建的 sanbox 大体是这样的

那么,我们是不是可以尝试,将非受信代码,通过 vm2 这个模块隔离在一个独立的进程中执行呢?然后,执行超时时,直接将隔离的进程干掉,但这里我们需要考虑如下几个问题

通过进程池统调度管理沙箱进程

如果来一个执行任务,创建一个进程,用完销毁,仅处理进程的开销就已经稍大了,并且也不能不设限的开新进程和宿主应用抢资源,那么,需要建一个进程池,所有任务到来会创建一个 Script 实例,先进入一个 pending 队列,然后直接将 script 实例的 defer 对象返回,调用处就能 await 执行结果了,然后由 sandbox master 根据工程进程的空闲程序来调度执行,master 会将 script 的执行信息,包括重要的 ScriptId,发送给空闲的 worker,worker 执行完成后会将「结果 + script 信息」回传给 master,master 通过 ScriptId 识别是哪个脚本执行完毕了,就是结果进行 resolve 或 reject 处理。

这样,通过「进程池」即能降低「进程来回创建和销毁的开销」,也能确保不过度抢占宿主资源,同时,在异步操作超时,还能将工程进程直接杀掉,同时,master 将发现一个工程进程挂掉,会立即创建替补进程。

处理的数据和结果,还有公开给沙箱的方法

进程间如何通讯,需要「动态代码」处理数据可以直接序列化后通过 IPC 发送给隔离 Sandbox 进程,执行结果一样经过序列化通过 IPC 传输。

其中,如果想法公开一个方法给 sandbox,因为不在一个进程,并不能方便的将一个方案的引用传递给 sandbox。我们可以将宿主的方法,在传递给 sandbox worker 之类做一下处理,转换为一个「描述对象」,包括了允许 sandbox 调用的方法信息,然后将信息,如同其它数据一样发送给 worker 进程,worker 收到数据后,识出来所「方法描述对象」,然后在 worker 进程中的 sandbox 对象上建立代理方法,代理方法同样通过 IPC 和 master 通讯。

最终,我们建立了一个大约这样的「沙箱环境」

如此这般处理起来是不是感觉很麻烦?但我们就有了一个更加安全一些的沙箱环境了,这些处理。笔者已经基于 TypeScript 编写,并封装为一个独立的模块 Safeify。

GitHub: https://github.com/Houfeng/safeify,欢迎 Star & Issues

最后,简单介绍一下 Safeify 如何使用,通过如下命令安装

npm i safeify --save
ログイン後にコピー

在应用中使用,还是比较简单的,如下代码(TypeScript 中类似)

import { Safeify } from './Safeify';
const safeVm = new Safeify({
 timeout: 50,     //超时时间,默认 50ms
 asyncTimeout: 500,  //包含异步操作的超时时间,默认 500ms
 quantity: 4      //沙箱进程数量,默认同 CPU 核数
});
const context = {
 a: 1, 
 b: 2,
 add(a, b) {
  return a + b;
 }
};
const rs = await safeVm.run(`return add(a,b)`, context);
console.log('result',rs);
ログイン後にコピー

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

ajax怎样动态增加表格的tr与td

react+antd做一个后台管理系统

以上がアプリケーションサンドボックス環境の活用の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Nodeのメモリ制御に関する記事 Nodeのメモリ制御に関する記事 Apr 26, 2023 pm 05:37 PM

ノンブロッキングおよびイベント駆動に基づいて構築されたノード サービスには、メモリ消費量が少ないという利点があり、大量のネットワーク リクエストの処理に非常に適しています。大量のリクエストを前提として、「メモリ制御」に関する問題を考慮する必要があります。 1. V8 のガベージ コレクション メカニズムとメモリ制限 Js はガベージ コレクション マシンによって制御されます

Node V8 エンジンのメモリと GC の詳細な図による説明 Node V8 エンジンのメモリと GC の詳細な図による説明 Mar 29, 2023 pm 06:02 PM

この記事では、NodeJS V8 エンジンのメモリとガベージ コレクター (GC) について詳しく説明します。

最適な Node.js Docker イメージを選択する方法について話しましょう。 最適な Node.js Docker イメージを選択する方法について話しましょう。 Dec 13, 2022 pm 08:00 PM

ノード用の Docker イメージの選択は些細なことのように思えるかもしれませんが、イメージのサイズと潜在的な脆弱性は、CI/CD プロセスとセキュリティに大きな影響を与える可能性があります。では、最適な Node.js Docker イメージを選択するにはどうすればよいでしょうか?

Node の File モジュールについて詳しく説明しましょう Node の File モジュールについて詳しく説明しましょう Apr 24, 2023 pm 05:49 PM

ファイル モジュールは、ファイルの読み取り/書き込み/開く/閉じる/削除の追加など、基礎となるファイル操作をカプセル化したものです。ファイル モジュールの最大の特徴は、すべてのメソッドが **同期** と ** の 2 つのバージョンを提供することです。 asynchronous**、sync サフィックスが付いているメソッドはすべて同期メソッドであり、持たないメソッドはすべて異種メソッドです。

Node.js 19 が正式リリースされました。その 6 つの主要な機能についてお話しましょう。 Node.js 19 が正式リリースされました。その 6 つの主要な機能についてお話しましょう。 Nov 16, 2022 pm 08:34 PM

Node 19 が正式リリースされましたので、この記事では Node.js 19 の 6 つの主要な機能について詳しく説明します。

Node.js の GC (ガベージ コレクション) メカニズムについて話しましょう Node.js の GC (ガベージ コレクション) メカニズムについて話しましょう Nov 29, 2022 pm 08:44 PM

Node.js はどのように GC (ガベージ コレクション) を行うのでしょうか?次の記事で詳しく説明します。

Nodeのイベントループについて話しましょう Nodeのイベントループについて話しましょう Apr 11, 2023 pm 07:08 PM

イベント ループは Node.js の基本的な部分であり、メイン スレッドがブロックされていないことを確認することで非同期プログラミングが可能になります。イベント ループを理解することは、効率的なアプリケーションを構築するために重要です。次の記事では、Node のイベント ループについて詳しく説明します。お役に立てれば幸いです。

ノードがnpmコマンドを使用できない場合はどうすればよいですか? ノードがnpmコマンドを使用できない場合はどうすればよいですか? Feb 08, 2023 am 10:09 AM

ノードが npm コマンドを使用できない理由は、環境変数が正しく設定されていないためです。解決策は次のとおりです: 1. 「システムのプロパティ」を開きます; 2. 「環境変数」->「システム変数」を見つけて、環境を編集します。変数; 3.nodejs フォルダーの場所を見つけます; 4.「OK」をクリックします。

See all articles