Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

execute untrusted code with custom permissions

License

NotificationsYou must be signed in to change notification settings

asvd/jailed

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Jailed is a small JavaScript library for running untrusted code in asandbox. The library is written in vanilla-js and has no dependencies.

With Jailed you can:

  • Load an untrusted code into a secure sandbox;

  • Export a set of external functions into the sandbox.

The untrusted code may then interract with the main application bydirectly calling those functions, but the application owner decideswhich functions to export, and therefore what will be allowed for theuntrusted code to perform.

The code is executed as aplugin, a special instance running as arestricted subprocess (in Node.js), or in a web-worker inside asandboxed frame (in case of web-browser environment). The iframe iscreated locally, so that you don't need to host it on a separate(sub)domain.

You can use Jailed to:

  • Setup a safe environment for executing untrusted code, without aneed to create a sandboxed worker / subprocess manually;

  • Do that in an isomorphic way: the syntax is same both for Node.jsand web-browser, the code works unchanged;

  • Execute a code from a string or from a file;

  • Initiate and interrupt the execution anytime;

  • Demo safelyexecute user-submitted code;

  • Demo embed3rd-party code and provide it the precise set of functions toharmlessly operate on the part of your application;

  • Export the particular set of application functions into the sandbox(or in the opposite direction), and let those functions be invokedfrom the other site (without a need for manual messaging) thusbuilding any custom API and set of permissions.

For instance:

varpath='http://path.to/the/plugin.js';// exported methods, will be available to the pluginvarapi={alert:alert};varplugin=newjailed.Plugin(path,api);

plugin.js:

// runs in a sandboxed worker, cannot access the main application,// with except for the explicitly exported alert() method// exported methods are stored in the application.remote objectapplication.remote.alert('Hello from the plugin!');

(exporting thealert() method is not that good idea actually)

Under the hood, an application may only communicate to a plugin(sandboxed worker / jailed subprocess) through a messaging mechanism,which is reused by Jailed in order to simulate the exporting ofparticular functions. Each exported function is duplicated on theopposite site with a special wrapper method with the same name. Uponthe wrapper method is called, arguments are serialized, and thecorresponding message is sent, which leads to the actual functioninvocation on the other site. If the executed function then issues acallback, the responce message will be sent back and handled by theopposite site, which will, in turn, execute the actual callbackpreviously stored upon the initial wrapper method invocation. Acallback is in fact a short-term exported function and behaves in thesame way, particularly it may invoke a newer callback in reply.

Installation

For the web-browser environment — download and unpack thedistribution, or install it usingBower:

$ bower install jailed

Load thejailed.js in a preferrable way. That is an UMD module, thusfor instance it may simply be loaded as a plain JavaScript file usingthe<script> tag:

<scriptsrc="jailed/jailed.js"></script>

For Node.js — install Jailed with npm:

$ npm install jailed

and then in your code:

varjailed=require('jailed');

Optionally you may load the script from thedistribution:

varjailed=require('path/to/jailed.js');

After the module is loaded, the two plugin constructors are available:jailed.Plugin andjailed.DynamicPlugin.

Usage

The messaging mechanism reused beyond the remote method invocationintroduces some natural limitations for the exported functions andtheir usage (nevertheless the most common use-cases are stillstraightforward):

  • Exported function arguments may only be either simple objects (whichare then serialized and sent within a message), or callbacks (whichare preserved and replaced with special identifiers beforesending). Custom object instance may not be used as an argument.

  • A callback can not be executed several times, it will be destroyedupon the first invocation.

  • If several callbacks are provided, only one of them may be called.

  • Returned value of an exported function is ignored, result should beprovided to a callback instead.

In Node.js thesend()method of a child process object is used for transfering messages,which serializes an object into a JSON-string. In a web-browserenvironment, the messages are transfered viapostMessage()method, which implementsthe structured clonealgorithmfor the serialization. That algorithm is more capable than JSON (forinstance, in a web-browser you may send a RegExp object, which is notpossible in Node.js).More details about structured clone algorithmand its comparsion toJSON.

A plugin object may be created either from a string containing asource code to be executed, or with a path to the script. To load aplugin code from a file, create the plugin usingjailed.Pluginconstructor and provide the path:

varpath='http://path.to/some/plugin.js';// set of methods to be exported into the pluginvarapi={alert:alert}varplugin=newjailed.Plugin(path,api);

plugin.js:

application.remote.alert('Hello from the plugin!');

Creating a plugin from a string containing a code is very similar,this is performed usingjailed.DynamicPlugin constructor:

varcode="application.remote.alert('Hello from the plugin!');";varapi={alert:alert}varplugin=newjailed.DynamicPlugin(code,api);

The secondapi argument provided to thejailed.Plugin andjailed.DynamicPlugin constructors is an interface object with a setof functions to be exported into the plugin. It is also possible toexport functions in the opposite direction — from a plugin to the mainapplication. It may be used for instance if a plugin provides a methodto perform a calculation. In this case the second argument of a pluginconstructor may be omitted. To export some plugin functions, useapplication.setInterface() method in the plugin code:

// create a pluginvarpath="http://path.to/some/plugin.js";varplugin=newjailed.Plugin(path);// called after the plugin is loadedvarstart=function(){// exported method is available at this pointplugin.remote.square(2,reportResult);}varreportResult=function(result){window.alert("Result is: "+result);}// execute start() upon the plugin is loadedplugin.whenConnected(start);

plugin.js:

// provides the method to square a numbervarapi={square:function(num,cb){// result reported to the callbackcb(num*num);}}// exports the api to the application environmentapplication.setInterface(api);

In this example thewhenConnected() plugin method is used at theapplication site: that method subscribes the given function to theplugin connection event, after which the functions exported by theplugin become accessible at theremote property of a plugin.

ThewhenConnected() method may be used as many times as needed andthus subscribe several handlers for a single connection event. Foradditional convenience, it is also possible to set a connectionhandler even after the plugin has already been connected — in thiscase the handler is issued immediately (yet asynchronously).

When a plugin code is executed, a set of functions exported by theapplication is already prepared. But if one of those functions isinvoked, it will actually be called on the application site. If inthis case the code of that function will try to use a functionexported by the plugin, it may not be prepared yet. To solve this, thesimilarapplication.whenConnected() method is available on theplugin site. The method works same as the one of the plugin object:the subscribed handler function will be executed after the connectionis initialized, and a set of functions exported by each site isavailable on the opposite site.

Therefore:

  • If you need to load a plugin and supply it with a set of exportedfunctions, simply provide those functions into the pluginconstructor, and then access those atapplication.remote propertyon the plugin site — the exported functions are already preparedwhen the plugin code is exectued.

  • If you need to load a plugin and use the functions it providesthrough exporting, set up a handler usingplugin.whenConnected()method on the application site. After the event is fired, thefunctions exported by the plugin are available at itsremoteproperty of the plugin object;.

  • If both application and a plugin use the exported functions of eachother,and the communication is initiated by the plugin, you willmost likely need to use theapplication.whenConnected() method onthe plugin site before initiating the communication, in order tomake sure that the functions exported by the plugin are alreadyavailable to the application.

To disconnect a plugin, use thedisconnect() method: it kills aworker / subprocess immediately without any chance for its code toreact.

A plugin may also disconnect itself by calling theapplication.disconnect() method.

In addition towhenConnected() method, the plugin object alsoprovides similarwhenFailed() andwhenDisconnected() methods:

  • whenFailed() subscribes a handler function to the connectionfailure event, which happens if there have been some error duringthe plugin initialization, like a network problem or a syntax errorin the plugin initialization code.

  • whenDisconnected() subscribes a function to the disconnect event,which happens if a plugin was disconnected by calling thedisconnect() method, or a plugin has disconnected itself bycallingapplication.disconnect(), or if a plugin failed toinitialize (along with the failure event mentioned above). After theevent is fired, the plugin is not usable anymore.

Just like as forwhenConnected() method, those two methods may alsobe used several times or even after the event has actually been fired.

Compatibility

Jailed was tested and should work in Node.js, and in the followingbrowsers:

  • Internet Explorer 10+, Edge
  • Firefox 26+
  • Opera 12+
  • Safari 6+
  • Chrome 10+

Security

This is how the sandbox is built:

In a web-browser:
  • asandboxediframeis created with itssandbox attribute only set to"allow-scripts"(to prevent the content of the frame from accessing anything of themain application origin);

  • then a web-worker is started inside that frame;

  • finally the code is loaded by the worker and executed.

Note: when Jailed library is loaded from the local source (its pathstarts withfile://), the"allow-same-origin" permission is addedto thesandbox attribute of the iframe. Local installations aremostly used for testing, and without that permission it would not bepossible to load the plugin code from a local file. This means thatthe plugin code has an access to the local filesystem, and to someorigin-shared things like IndexedDB (though the main application pageis still not accessible from the worker). Therefore if you need tosafely execute untrusted code on a local system, reuse the Jailedlibrary in Node.js.

In Node.js:

Warning: according to recent reports(#33) this way ofsandboxing is not secure any longer, the fix is being prepared...

  • A Node.js subprocess is created by the Jailed library;

  • the subprocess (down)loads the file containing an untrusted code asa string (or, in case ofDynamicPlugin, simply uses the providedstring with code)

  • then"use strict"; is appended to the head of that code (in orderto prevent breaking the sandbox usingarguments.callee.caller);

  • finally the code is executed usingvm.runInNewContext() method,where the provided sandbox only exposes some basic methods likesetTimeout(), and theapplication object for messaging with theapplication site.

--

follow me on twitter:https://twitter.com/asvd0

About

execute untrusted code with custom permissions

Resources

License

Stars

Watchers

Forks

Packages

No packages published

[8]ページ先頭

©2009-2025 Movatter.jp