Skip to content

kmacrow/Nerio

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

31 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation


Nerio is a "safe" subset of JavaScript, not unlike Adsafe, that helps you run untrusted code without
allowing it access to various language and browser features.

Background

Sometimes you would like to allow third-party code to run as part of a page or application. Unfortunately, it's hard to know what nefarious things someone else's code might do. Nerio defines a subset of JavaScript and provides some simple tools for checking that an arbitrary piece of JavaScript code is valid Nerio code. Static analysis has the benefit that once code is checked, it can run at full speed whether in the browser or a server-side engine. Alternatives involving JavaScript interpreters compiled to asm.js can be two orders of magnitude slower. WebWorker sandboxes require a virtual DOM, which is also potentially very slow.

Overview

Nerio provides a simple command line tool as well as several programmatic interfaces for statically checking fragments of JavaScript in memory or files. Nerio checks for use of features or APIs that might enable exfiltration of private data, communication with untrusted servers or attempts to interpose on core browser APIs. Much like a compiler, Nerio can provide detailed line-level warnings and errors when it detects unsafe code. As a pure JavaScript implementation, the entire tool and API can be used from a web page, or in standalone applications via node.js.

Implementation

Nerio is implemented entirely in JavaScript. The excellent Esprima parsing engine is used to parse raw JavaScript code into the standard Mozilla SpiderMonkey AST data structure. esmangle is used to "compress" (remove redundancy, dead code, and normalize syntax in) the AST structure. Nerio then makes several passes over the AST looking for function call sites, identifiers, and symbols that look suspicious. Nerio does not fail early: instead it looks at all of the code and reports any problems found, which is potentially useful for debugging repeated violations or to derive a sense of just how problematic the code is. Internally, Nerio is designed to be modular: modules that implement different checks register themselves with a central engine which orchestrates their execution over the AST and collects violations in a standard way. We are also working on some experimental code-rewriting features in which we rewrite the AST on the fly to wrap suspicious code in runtime checks and then use escodegen to generate JavaScript from the transformed AST.

Features

We inspect a number of language and browser API features. Our approach is to compress/optimize the AST before searching for problematic features so that the number of cases (or corner cases) is minimized. JavaScript's syntactic flexibility can manifest use of one feature in the same way as several different ASTs, compressing the AST generally converts all instances to a single, optimal structure.

Language

  1. eval() enables arbitrary code execution.
  2. Use of obj[key] object access when obj is special (see below) and key is not literal.

Browser API

References to window, XMLHttpRequest, document, and WebWorker.

The above global objects or classes enable manipulation of the DOM, direct or indirect initiation of network requests (think image src attribute) or create new code execution flows. While this might seem like a short list, these features and API objects represent most of the surface that a potentially malicious script could make use of. As it turns out, detecting potential references to these objects and functions in JavaScript is fairly challenging.

Related work

There is a substantial body of work related to Nerio, from the research community as well as industry and open source. Below is a brief summary of several key projects. Gatekeeper in particular enumerates a lot of the JavaScript language and browser API features that we are interested in restricting or disabling altogether. Treehouse provides a lightweight virtual DOM and runtime isolation (via WebWorkers) that we will likely combine with Nerio in future work.

Virtual runtimes

js.js - GitHub page
A JS interpreter in JS, so you can sandbox while you sandbox.

Treehouse - ATC '12 paper
Loads a "broker" into a WebWorker. Interposes on browser APIs, freezes them, then runs code against a virtual DOM via message passing with main thread.

Static analysis

FBJS - Developer page
Facebook App JS subset (proprietary)

Gatekeeper - USENIX SEC '09 paper
Static analysis + code rewriting to safe subset of JS

Adsafety - Website
Verifying static checkers (in this case Adsafe)

Adsafe - adsafe.org (GitHub page)
A subset of JS safe for embedding in ads

Browsershield - OSDI '06 paper
Complete JS/HTML rewriting to safe widgets

Websandbox - websandbox.org
Similar to Browsershield (also by MS), rewrites all web content into safe widgets

Caja - Google Code
Ca-pabilities based Ja-vascript. Weird, supposed to pronounce as “Caha” from the Spanish “safe” or “box”. Rewrites html, css, JavaScript to a safe version.

Conclusions

So far Nerio is a humble prototype, only a shadow of what we have planned for it. That said, the flexibility and effectiveness of our prototype is promising and we hope to continue building it out into a more sophisticated and practicable tool as future work. Static analysis at the AST level is quite difficult, and especially so for a dynamic, mixed-paradigm language like JavaScript. We hope that eventually Nerio will enable a class of browser-based applications that can make stronger guarantees about where our personal data can, and cannot, end up.

Getting started

To get started with Nerio you should have the latest Node.js (including npm) installed on your system. There is a devenv script included that will install it for you on unix-like platforms. Note: you need to download node and npm directly from nodejs.org because the version tracked by most package managers is too old.

npm install -g nerio

Then you can use nerio on the command line,

$ echo "var PI = 3.14;" | nerio

or pass it a file to check:

$ nerio samples/eval.js
Failed:
EVAL: Explicit call to eval() at 2:0
EVAL: Assignment to result of eval() at 4:8
EVAL: Assignment to result of eval() at 4:22
...

or the programmatic API:

nerio = require('nerio');
nerio.check_code(['js/script-to-check.js'], function(success, err) {
	if( !success )
		console.log(err);
	else
		console.log('Success!');
});

There is also an API compiled for use directly in the browser.

<script src="//nerio/dist/nerio.min.js"></script>
<script>
	var code = '...';
	nerio.check_code(code, function(success, err) { ... }); 
</script>



Nerio is licensed under the GPL v3.

Copyright © 2013-14 RJ Sumi and Kalan MacRow (@k16w)