Avoiding Eval in javascript call to Pannellum - javascript

I have a website in which I use javascript and pannellum API to load multiple 360 panoramic views.
Occasionally the browser crashes, particularly when on my iPhone 6 in VR mode when a total of six pannellum instances are required in different DIVs.
As best I can tell, the browser crashes on the call to pannellum which I am doing from inside an eval function as the data I pass to pannellum is contained in a variable.
Here's the call, plus the subsequent line that lets us know that the panorama is loaded.
eval("RightPanoInt=pannellum.viewer('RightPanoIntermediary', " + PanoDataIntermediary +");");
RightPanoInt.on("load", function() { LoadedRightPanoIntermediary(); });
Where
RightPanoInt is a variable I can use to check if the panorama is loaded
RightPanoIntermediary is the id of the DIV the panorama is to be loaded into.
and
PanoDataIntermediary is a variable containing the data / parameters need by the pannellum API.
For example
{"autoLoad": true,"pitch": 0,"yaw": 73,"vaov": 180,"haov": 360,"vOffset": 0,"maxPitch": 90,"minPitch": -90,"maxYaw": 180,"minYaw": -180,"maxHfov": 100,"hfov": 100,"minHfov": 10,"showFullscreenCtrl": false,"orientationOnByDefault": false,"showControls": false,"panorama": "002.jpg","preview":"BackgroundPaleGreen.jpg"}
The data will be different the next time the call is made, so the parameters must be in the PanoDataIntermediary variable.
What alternative method to the eval function can I use to make the same call?

Okay, the responses above have made me realise that instead of using a variable, I really should be using an object. For example...
var PanoDataIntermediary={"type":"equirectangular"}; // to initiate
PanoDataIntermediary.panorama="Church_HDR_x4_000.jpg"; // to add more parameters
PanoDataIntermediary.autoLoad = true;
PanoDataIntermediary.pitch=0;
PanoDataIntermediary.yaw=73;
PanoDataIntermediary.hoav=360; // etc
RightPanoInt=pannellum.viewer('panorama', PanoDataIntermediary);
This will work without the need for using eval. It will take me awhile to rehash my existing code to see if it resolves the browser crash problem.
Thanks again.

Have you tried this (without eval)?
var RightPanoInt = pannellum.viewer('RightPanoIntermediary', PanoDataIntermediary);
According to your question, PanoDataIntermediary is a variable holding data. The only reason you'd need eval is if PanoDataIntermediary merely held the name of another variable that held the data.
Furthermore, the statement eval("RightPanoInt=pannellum.viewer('RightPanoIntermediary', " + PanoDataIntermediary +");"); treats your PanoDataIntermediary object as if it were a string but it is not a string and so cannot be concatenated as a string. This may be causing your browser crash. At the very least, it's throwing an exception.
A sample use of eval:
var myVariable = {a: 1, b:2, c:'test'};
var myVariableName = 'myVariable';
alert(eval(myVariableName).c);
Note: I'm not promoting the use of eval here. I'm just trying to offer some context.
Hope that helps.

Related

Securing JavaScript eval function

We want to give our users the ability to execute self created JavaScript code within our application. For this we need to use eval to evaluate the code. To reduce all security concerns to a minimum (if not zero), our idea is to prevent the usage of any window or document function within the code. So no XMLHttpRequest or anything similar.
This is the code:
function secure_eval(s) {
var ret;
(function(){
var copyXMLHttpRequest = XMLHttpRequest; // save orginal function in copy
XMLHttpRequest = undefined; // make orignal function unavailable
(function() {
var copyXMLHttpRequest; // prevent access to copy
try {
ret = eval(s)
} catch(e) {
console.log("syntax error or illegal function used");
}
}())
XMLHttpRequest = copyXMLHttpRequest; // restore original function
}())
return ret;
}
This works as follows:
secure_eval('new XMLHttpRequest()'); // ==> "illegal function used"
Now I have several questions:
Is this pattern the right way to secure eval?
What functions of window and document are the ones which are considered harmful?
To ship around question 2. I tried to mask all (native) functions of window But I am not able to enumerate them:
This does not list XMLHttpRequest for instance:
for( var x in window) {
if( window[x] instanceof Function) {
console.log(x);
}
}
Is there a way to get a list of all native functions of window and document?
EDIT:
One of my ideas is to perform the eval within a Worker and prevent access to XMLHttpRequest and document.createElement (see my solution above). This would have (to my mind) the following consequences:
no access to the original document
no access to the original window
no chance to communicate with external resources (no ajax, no scripts)
Do you see any drawback or leaks here?
EDIT2:
In the meantime I have found this question which answer solves many of my problems plus a couple of things I did not even think about (i.e. browser dead lock with "while(true){}".
Your code does not actually prevent the use of XMLHttpRequest. I can instantiate an XMLHttpRequest object with these methods:
secure_eval("secure_eval = eval"); // Yep, this completely overwrites secure_eval.
secure_eval("XMLHttpRequest()");
Or:
secure_eval("new (window.open().XMLHttpRequest)()")
Or:
secure_eval("new (document.getElementById('frame').contentWindow.XMLHttpRequest)()")
This 3rd method relies on the presence of an iframe in the HTML of the page, which someone could add by manipulating the DOM in their browser. I do such manipulations every now and then with Greasemonkey to remove annoyances or fix broken GUIs.
This took me about 5 minutes to figure out, and I am not by any means a security guru. And these are only the holes I was able to find quickly, there are probably others, that I don't know about. The lesson here is that it is really really really hard to secure code through eval.
Using A Worker
Ok, so using a Worker to run the code is going to take care of the 2nd and 3rd cases above because there's no window accessible in a Worker. And... hmm.. the 1st case can be handled by shadowing secure_eval inside its scope. End of story? If only...
If I put secure_eval inside a web worker and run the following code, I can reacquire XMLHttpRequest:
secure_eval("var old_log = console.log; console.log = function () { foo = XMLHttpRequest; old_log.apply(this, arguments); };");
console.log("blah");
console.log(secure_eval("foo"));
The principle is to override a function that is used outside secure_eval to capture XMLHttpRequest by assigning it to a variable that will be deliberately leaked to the global space of the worker, wait until that function is used by the worker outside secure_eval, and then grab the saved value. The first console.log above simulates the use of the tampered function outside secure_eval and the 2nd console.log shows that the value was captured. I've used console.log because why not? But really any function in the global space could be modified like this.
Actually, why wait until the worker may use some function we tampered with? Here's another, better, quicker way to do access XMLHttpRequest:
secure_eval("setTimeout(function () { console.log(XMLHttpRequest);}, 0);");
Even in a worker (with a pristine console.log), this will output the actual value of XMLHttpRequest to the console. I'll also note that the value of this inside the function passed to setTimeout is the global scope object (i.e. window when not in a worker, or self in a worker), unaffected by any variable shadowing.
What About the Other Question Mentioned in This Question?
What about the solution here? Much much better but there is still a hole when run in Chrome 38:
makeWorkerExecuteSomeCode('event.target.XMLHttpRequest',
function (answer) { console.log( answer ); });
This will show:
function XMLHttpRequest() { [native code] }
Again, I'm no security guru or cracker bent on causing trouble. There are probably still more ways I'm not thinking about.
I'll try and answer your questions in order here.
Is this pattern the right way to secure eval?
This part is slightly subjective. I don't see any major security drawbacks to this. I tried several ways to access XMLHttpRequest, but i couldn't:
secure_eval('XMLHttpRequest')
secure_eval('window.XMLHttpRequest')
secure_eval('eval("XMLHttpRequest")()')
secure_eval('window.__proto__.XMLHttpRequest') // nope, it's not inherited
However, it will be a lot if you want to blacklist more things.
What functions of window and document are the ones which are considered harmful?
That depends on what you consider "harmful". Is it bad if the DOM is accessible at all? Or what about WebKit desktop notifications, or speech synthesis?
You'll have to decide this based on your specific use case.
To ship around question 2. I tried to mask all (native) functions of window, but I am not able to enumerate them:
That's because most of the methods are non-enumerable. To enumerate, you can use Object.getOwnPropertyNames(window):
var globals = Object.getOwnPropertyNames(window);
for (var i = 0; i < globals.length; i++) {
if( window[globals[i]] instanceof Function) {
console.log(globals[i]);
}
}
One of my ideas is to perform the eval within a Worker and prevent access to XMLHttpRequest and document.createElement (see my solution above).
This sounds like a good idea.
I stumbled across a really, really nice blog article about the notorious Eval here. The article does discuss in detail. You won't be able to alleviate all security concerns, but you can prevent Cross-Script Attacks by building tokens for the input. This would in theory prevent malicious code that could be harmful from being introduced.
Your only other hurdle will be Man-In-The-Middle Attacks. I'm not sure if that would be possible, as you can't trust input and output.
The Mozilla Developer Network does explicitly state:
eval() is a dangerous function, which executes the code it's passed
with the privileges of the caller. If you run eval() with a string
that could be affected by a malicious party, you may end up running
malicious code on the user's machine with the permissions of your
webpage / extension. More importantly, third party code can see the
scope in which eval() was invoked, which can lead to possible attacks
in ways to which the similar Function is not susceptible.
eval() is also generally slower than the alternatives, since it has to
invoke the JS interpreter, while many other constructs are optimized
by modern JS engines.
There are safer (and faster!) alternatives to eval() for common
use-cases.
I'm slightly against Eval and truly try to use it when warranted.
I have stated it yet in my question, but to make it more clear I will post it as an answer also:
I think the accepted answer on this question is the correct and only way to completely isolate and constrain eval().
It is also secure against these hacks:
(new ('hello'.constructor.constructor)('alert("hello from global");'))()
(function(){return this;})().alert("hello again from global!");
while(true){} // if no worker --> R.I.P. browser tab
Array(5000000000).join("adasdadadasd") // memory --> boom!
There was a question long ago much like this. So I dusted off some old code and fixed it up.
It essentially works by taking advantage of the with keyword and providing it with a frozen empty object. The prototype of the empty object is filled with null properties, the keys of which match the names global variables like self, window and their enumerable property keys; The prototype object is also frozen. eval is then called within the with statement (Almost the same way that scripts run with an implicit with(window){} block if I understand correctly). When you try to access window or its properties you get redirected (via the with block) to null versions (with same key) found in empty object (or rather the empty object's prototype):
function buildQuarantinedEval(){
var empty=(function(){
var exceptionKeys = [
"eval", "Object", //need exceptions for these else error. (ie, 'Exception: redefining eval is deprecated')
"Number", "String", "Boolean", "RegExp", "JSON", "Date", "Array", "Math",
"this",
"strEval"
];
var forbiddenKeys=["window","self"];
var forbidden=Object.create(null);
[window,this,self].forEach(function(obj){
Object.getOwnPropertyNames(obj).forEach(function(key){
forbidden[key]=null;
});
//just making sure we get everything
Object.keys(obj).forEach(function(key){
forbidden[key]=null;
});
for(var key in obj){
forbidden[key]=null;
}
});
forbiddenKeys.forEach(function(key){
forbidden[key]=null;
});
exceptionKeys.forEach(function(key){
delete forbidden[key];
});
Object.freeze(forbidden);
var empty=Object.create(forbidden);
Object.freeze(empty);
return empty;
})();
return function(strEval){
return (function(empty,strEval){
try{
with(empty){
return eval(strEval);
}
}
catch(err){
return err.message;
}
}).call(empty,empty,strEval);
};
}
Setup by building a function/closure that evaluates some expression:
var qeval=buildQuarantinedEval();
qeval("'some expression'"); //evaluate
Tests:
var testBattery=[
"'abc'","8*8","console","window","location","XMLHttpRequest",
"console","eval('1+1+1')","eval('7/9+1')","Date.now()","document",
"/^http:/","JSON.stringify({a:0,b:1,c:2})","HTMLElement","typeof(window)",
"Object.keys(window)","Object.getOwnPropertyNames(window)",
"var result; try{result=window.location.href;}catch(err){result=err.message;}; result;",
"parseInt('z')","Math.random()",
"[1,2,3,4,8].reduce(function(p,c){return p+c;},0);"
];
var qeval=buildQuarantinedEval();
testBattery.map(function(code){
const pad=" ";
var result= qeval(code);
if(typeof(result)=="undefined")result= "undefined";
if(result===null)result= "null";
return (code+pad).slice(0,16)+": \t"+result;
}).join("\n");
Results:
/*
'abc' : abc
8*8 : 64
console : null
window : null
location : null
XMLHttpRequest : null
console : null
eval('1+1+1') : 3
eval('7/9+1') : 1.7777777777777777
Date.now() : 1415335338588
document : null
/^http:/ : /^http:/
JSON.stringify({: {"a":0,"b":1,"c":2}
HTMLElement : null
typeof(window) : object
Object.keys(wind: window is not an object
Object.getOwnPro: can't convert null to object
var result; try{: window is null
parseInt('z') : parseInt is not a function
Math.random() : 0.8405481658901747
[1,2,3,4,8].redu: 18
*/
Notes: This technique can fail when some properties of window are defined late (after initializing/creating our quarantined eval function). In the past, I've noticed some property keys are not enumerated until after you access the property, after which Object.keys or Object.getOwnPropertyNames will finally be able grab their keys. On the other hand this technique can also be quite aggressive in blocking objects/functions you do not want blocked (an example would be like parseInt); In these cases, you'll need to manually add global objects/functions that you do want into the exceptionKeys array.
*edit* Additional considerations: How well this all performs depends entirely on how well the mask matches that of the property keys of the window object. Any time you add an element to the document and give it a new ID, you just inserted a new property into the global window object, potentially allowing our 'attacker' to grab it and break out of the quarantine/firewall we've setup (i.e. access element.querySelector then eventually window obj from there). So the mask (i.e., the variable forbidden) either needs to be updated constantly perhap with watch method or rebuilt each time; The former conflicts with the necessity of the mask to have a frozen interface, and the latter is kinda expensive having to enumerate all the keys of window for each evaluation.
Like I said earlier, this is mostly old code I was working on, then abandoned, that was quickly fixed up on short order. So it's not by any means thoroughly tested. I'll leave that to you.
and a jsfiddle
I have small idea about secure eval for small or limited things if you know well what u going to use eval in you can create white list and black list and excute only the strings that has the valid but it good for small covered app for example calculator has few options (x, y) and (+,*,-,/) if i added this characters in white list and add check for script length and study what excepted length of the script run it can be secure and no one can pass that
const x = 5;
const y = 10;
function secureEval(hack_string){
// 0 risk eval calculator
const whiteList = ['',' ', 'x', 'y','+','*','/','-'];
for (let i=0; i<hack_string.length; i++){
if (!whiteList.includes(hack_string[i])){
return 'Sorry u can not hack my systems';
}
}
return 'good code system identify result is : ' + eval(hack_string);
}
// bad code
document.getElementById("secure_demo").innerHTML = secureEval('x * y; alert("hacked")');
document.getElementById("demo").innerHTML = secureEval('x * y');
<!DOCTYPE html>
<html>
<body>
<h1>Secure Eval</h1>
<p id="secure_demo"></p>
<p id="demo"></p>
</body>
</html>

Deleting large Javascript objects when process is running out of memory

I'm a novice to this kind of javascript, so I'll give a brief explanation:
I have a web scraper built in Nodejs that gathers (quite a bit of) data, processes it with Cheerio (basically jQuery for Node) creates an object then uploads it to mongoDB.
It works just fine, except for on larger sites. What's appears to be happening is:
I give the scraper an online store's URL to scrape
Node goes to that URL and retrieves anywhere from 5,000 - 40,000 product urls to scrape
For each of these new URLs, Node's request module gets the page source then loads up the data to Cheerio.
Using Cheerio I create a JS object which represents the product.
I ship the object off to MongoDB where it's saved to my database.
As I say, this happens for thousands of URLs and once I get to, say, 10,000 urls loaded I get errors in node. The most common is:
Node: Fatal JS Error: Process out of memory
Ok, here's the actual question(s):
I think this is happening because Node's garbage cleanup isn't working properly. It's possible that, for example, the request data scraped from all 40,000 urls is still in memory, or at the very least the 40,000 created javascript objects may be. Perhaps it's also because the MongoDB connection is made at the start of the session and is never closed (I just close the script manually once all the products are done). This is to avoid opening/closing the connection it every single time I log a new product.
To really ensure they're cleaned up properly (once the product goes to MongoDB I don't use it anymore and can be deleted from memory) can/should I just simply delete it from memory, simply using delete product?
Moreso (I'm clearly not across how JS handles objects) if I delete one reference to the object is it totally wiped from memory, or do I have to delete all of them?
For instance:
var saveToDB = require ('./mongoDBFunction.js');
function getData(link){
request(link, function(data){
var $ = cheerio.load(data);
createProduct($)
})
}
function createProduct($)
var product = {
a: 'asadf',
b: 'asdfsd'
// there's about 50 lines of data in here in the real products but this is for brevity
}
product.name = $('.selector').dostuffwithitinjquery('etc');
saveToDB(product);
}
// In mongoDBFunction.js
exports.saveToDB(item){
db.products.save(item, function(err){
console.log("Item was successfully saved!");
delete item; // Will this completely delete the item from memory?
})
}
delete in javascript is NOT used to delete variables or free memory. It is ONLY used to remove a property from an object. You may find this article on the delete operator a good read.
You can remove a reference to the data held in a variable by setting the variable to something like null. If there are no other references to that data, then that will make it eligible for garbage collection. If there are other references to that object, then it will not be cleared from memory until there are no more references to it (e.g. no way for your code to get to it).
As for what is causing the memory accumulation, there are a number of possibilities and we can't really see enough of your code to know what references could be held onto that would keep the GC from freeing up things.
If this is a single, long running process with no breaks in execution, you might also need to manually run the garbage collector to make sure it gets a chance to clean up things you have released.
Here's are a couple articles on tracking down your memory usage in node.js: http://dtrace.org/blogs/bmc/2012/05/05/debugging-node-js-memory-leaks/ and https://hacks.mozilla.org/2012/11/tracking-down-memory-leaks-in-node-js-a-node-js-holiday-season/.
JavaScript has a garbage collector that automatically track which variable is "reachable". If a variable is "reachable", then its value won't be released.
For example if you have a global variable var g_hugeArray and you assign it a huge array, you actually have two JavaScript object here: one is the huge block that holds the array data. Another is a property on the window object whose name is "g_hugeArray" that points to that data. So the reference chain is: window -> g_hugeArray -> the actual array.
In order to release the actual array, you make the actual array "unreachable". you can break either link the above chain to achieve this. If you set g_hugeArray to null, then you break the link between g_hugeArray and the actual array. This makes the array data unreachable thus it will be released when the garbage collector runs. Alternatively, you can use "delete window.g_hugeArray" to remove property "g_hugeArray" from the window object. This breaks the link between window and g_hugeArray and also makes the actual array unreachable.
The situation gets more complicated when you have "closures". A closure is created when you have a local function that reference a local variable. For example:
function a()
{
var x = 10;
var y = 20;
setTimeout(function()
{
alert(x);
}, 100);
}
In this case, local variable x is still reachable from the anonymous time out function even after function "a" has returned. If without the timeout function, then both local variable x and y will become unreachable as soon as function a returns. But the existence of the anonymous function change this. Depending on how the JavaScript engine is implemented, it may choose to keep both variable x and y (because it doesn't know whether the function will need y until the function actually runs, which occurs after function a returns). Or if it is smart enough, it can only keep x. Imagine that if both x and y points to big things, this can be a problem. So closure is very convenient but at times it is more likely to cause memory issues and can make it more difficult to track memory issues.
I faced same problem in my application with similar functionality. I've been looking for memory leaks or something like that. The size of consumed memory my process has reached to 1.4 GB and depends on the number of links that must be downloaded.
The first thing I noticed was that after manually running the Garbage Collector, almost all memory was freed. Each page that I downloaded took about 1 MB, was processed and stored in the database.
Then I install heapdump and looked at the snapshot of the application. More information about memory profiling you can found at Webstorm Blog.
My guess is that while the application is running, the GC does not start. To do this, I began to run application with the flag --expose-gc, and began to run GC manually at the time of implementation of the program.
const runGCIfNeeded = (() => {
let i = 0;
return function runGCIfNeeded() {
if (i++ > 200) {
i = 0;
if (global.gc) {
global.gc();
} else {
logger.warn('Garbage collection unavailable. Pass --expose-gc when launching node to enable forced garbage collection.');
}
}
};
})();
// run GC check after each iteration
checkProduct(product._id)
.then(/* ... */)
.finally(runGCIfNeeded)
Interestingly, if you do not use const, let, var, etc when you define something in the global scope, it seems be an attribute of the global object, and deleting returns true. This could cause it to be garbage collected. I tested it like this and it seems to have the intended impact on my memory usage, please let me know if this is incorrect or if you got drastically different results:
x = [];
process.memoryUsage();
i = 0;
while(i<1000000) {
x.push(10.5);
}
process.memoryUsage();
delete x
process.memoryUsage();

Extract a value from a page's function... ¿Without unsafeWindow?

I'm new at Greasemonkey. I'm trying to create a user script to improve my user experience with a site I often visit.
So I'd like to create a javascript function f(). My function requires a value that, according to my quick research and try at understanding the site's structure, only exists inside a javascript function g().
I am not sure of which file holds g() on the server, but what I do know is that once the page has finished downloading, g() can be used.
I would like to extract the string value from g() without executing it (with g.toString()).
My question is: how can I access g(), without compromising my computer with unsafeWindow?
(window.g returns null inside the Greasemonkey script).
You have to use unsafeWindow. It's really not that evil.
var string = unsafeWindow.g.toString();
unsafeWindow is relatively safe. I have previously discovered a method to access an unrestricted window object in GreaseMonkey. Using a specific method, it's possible to read the original code of the Userscript by the affected page. Specific GreaseMonkey functions (GM_getValue, ..) cannot be used though: Advanced GreaseMonkey: Using constructors/methods/variables at a remote page
EDIT, regarding the title change
If you fear that g is not a function, or that the toString method of the function is overwritten, use the following code:
//Store unsafeWindow.g in a variable, to reduce the possibly defined
// __defineGetter__ calls to a minimum.
var g_string = unsafeWindow.g;
if(typeof g_string == "function"){
g_string = Function.prototype.toString.call(g_string);
}
else g_string = ""; //Reset
Is it safe?
The previous code is the safest approach, because no methods of g are invoked. The GreaseMonkey wrapper also prevents the affected page from reading the script:
window.g = function(){}
window.__defineGetter__("g", function(){
alert(arguments.callee.caller);
});
The previous code will not show the real caller, but an useless wrapper function:
function SJOWContentBoundary() {
[native code]
}

Is there a way to jail in Javascript, so that the DOM isn't visible

I would really like to provide the user some scripting capabilities, while not giving it access to the more powerful features, like altering the DOM. That is, all input/output is tunneled thru a given interface. Like a kind of restricted javacsript.
Example:
If the interface is checkanswer(func)
this are allowed:
checkanswer( function (x,y)={
return x+y;
}
but these are not allowed:
alert(1)
document.write("hello world")
eval("alert()")
EDIT: what I had in mind was a simple language that was implemented using javascript, something like http://stevehanov.ca/blog/index.php?id=92
(Edit This answer relates to your pre-edit question. Don't know of any script languages implemented using Javascript, although I expect there are some. For instance, at one point someone wrote BASIC for Javascript (used to have a link, but it rotted). The remainder of this answer is therefore pretty academic, but I've left it just for discussion, illustration, and even cautionary purposes. Also, I definitely agree with bobince's points — don't do this yourself, use the work of others, such as Caja.)
If you allow any scripting in user-generated content, be ready for the fact you'll be entering an arms race of people finding holes in your protection mechanisms and exploiting them, and you responding to those exploits. I think I'd probably shy away from it, but you know your community and your options for dealing with abuse. So if you're prepared for that:
Because of the way that Javascript does symbol resolution, it seems like it should be possible to evaluate a script in a context where window, document, ActiveXObject, XMLHttpRequest, and similar don't have their usual meanings:
// Define the scoper
var Scoper = (function() {
var rv = {};
rv.scope = function(codeString) {
var window,
document,
ActiveXObject,
XMLHttpRequest,
alert,
setTimeout,
setInterval,
clearTimeout,
clearInterval,
Function,
arguments;
// etc., etc., etc.
// Just declaring `arguments` doesn't work (which makes
// sense, actually), but overwriting it does
arguments = undefined;
// Execute the code; still probably pretty unsafe!
eval(codeString);
};
return rv;;
})();
// Usage:
Scoper.scope(codeString);
(Now that uses the evil eval, but I can't immediately think of a way to shadow the default objects cross-browser without using eval, and if you're receiving the code as text anyway...)
But it doesn't work, it's only a partial solution (more below). The logic there is that any attempt within the code in codeString to access window (for instance) will access the local variable window, not the global; and the same for the others. Unfortunately, because of the way symbols are resolved, any property of window can be accessed with or without the window. prefix (alert, for instance), so you have to list those too. This could be a long list, not least because as bobince points out, IE dumps any DOM element with a name or an ID onto window. So you'd probably have to put all of this in its own iframe so you can do an end-run around that problem and "only" have to deal with the standard stuff. Also note how I made the scope function a property of an object, and then you only call it through the property. That's so that this is set to the Scoper instance (otherwise, on a raw function call, this defaults to window!).
But, as bobince points out, there are just so many different ways to get at things. For instance, this code in codeString successfully breaks the jail above:
(new ('hello'.constructor.constructor)('alert("hello from global");'))()
Now, maybe you could update the jail to make that specific exploit not work (mucking about with the constructor properties on all — all — of the built-in objects), but I tend to doubt it. And if you could, someone (like Bob) would just come up with a new exploit, like this one:
(function(){return this;})().alert("hello again from global!");
Hence the "arms race."
The only really thorough way to do this would be to have a proper Javascript parser built into your site, parse their code and check for illegal accesses, and only then let the code run. It's a lot of work, but if your use-case justifies it...
T.J. Crowder makes an excellent point about the "arms race." It's going to be very tough to build a watertight sandbox.
it's possible to override certain functions, though, quite easily.
Simple functions:
JavaScript: Overriding alert()
And according to this question, even overriding things like document.write is as simple as
document.write = function(str) {}
if that works in the browsers you need to support (I assume it works in all of them), that may be the best solution.
Alternative options:
Sandboxing the script into an IFrame on a different subdomain. It would be possible to manipulate its own DOM and emit alert()s and such, but the surrounding site would remain untouched. You may have to do this anyway, no matter which method(s) you choose
Parsing the user's code using a white list of allowed functions. Awfully complex to do as well, because there are so many notations and variations to take care of.
There are several methods to monitor the DOM for changes, and I'm pretty sure it's possible to build a mechanism that reverts any changes immediately, quite similar to Windows's DLL management. But it's going to be awfully complex to build and very resource-intensive.
Not really. JavaScript is an extremely dynamic language with many hidden or browser-specific features that can be used to break out of any kind of jail you can devise.
Don't try to take this on yourself. Consider using an existing ‘mini-JS-like-language’ project such as Caja.
Sounds like you need to process the user entered data and replace invalid mark-up based on a white list or black-list of allowed content.
You can do it the same way as Facebook did. They're preprocessing all the javascript sources, adding a prefix to all the names other than their own wrapper APIs'.
I got another way: use google gears WorkerPool api
See this
http://code.google.com/apis/gears/api_workerpool.html
A created worker does not have access
to the DOM; objects like document and
window exist only on the main page.
This is a consequence of workers not
sharing any execution state. However,
workers do have access to all
JavaScript built-in functions. Most
Gears methods can also be used,
through a global variable that is
automatically defined:
google.gears.factory. (One exception
is the LocalServer file submitter,
which requires the DOM.) For other
functionality, created workers can ask
the main page to carry out requests.
What about this pattern in order to implement a sandbox?
function safe(code,args)
{
if (!args)
args=[];
return (function(){
for (i in window)
eval("var "+i+";");
return function(){return eval(code);}.apply(0,args);
})();
}
ff=function()
{
return 3.14;
}
console.log(safe("this;"));//Number
console.log(safe("window;"));//undefined
console.log(safe("console;"));//undefined
console.log(safe("Math;"));//MathConstructor
console.log(safe("JSON;"));//JSON
console.log(safe("Element;"));//undefined
console.log(safe("document;"));//undefined
console.log(safe("Math.cos(arguments[0]);",[3.14]));//-0.9999987317275395
console.log(safe("arguments[0]();",[ff]));//3.14
That returns:
Number
undefined
undefined
MathConstructor
JSON
undefined
undefined
-0.9999987317275395
3.14
Can you please provide an exploit suitable to attack this solution ? Just to understand and improve my knowledge, of course :)
THANKS!
This is now easily possible with sandboxed IFrames:
var codeFunction = function(x, y) {
alert("Malicious code!");
return x + y;
}
var iframe = document.createElement("iframe");
iframe.sandbox = "allow-scripts";
iframe.style.display = "none";
iframe.src = `data:text/html,
<script>
var customFunction = ${codeFunction.toString()};
window.onmessage = function(e) {
parent.postMessage(customFunction(e.data.x, e.data.y), '*'); // Get arguments from input object
}
</script>`;
document.body.appendChild(iframe);
iframe.onload = function() {
iframe.contentWindow.postMessage({ // Input object
x: 5,
y: 6
}, "*");
}
window.onmessage = function(e) {
console.log(e.data); // 11
document.body.removeChild(iframe);
}

How can I prevent auto-parsed, AJAX-gained JSON to become a window variable?

I'm using JSON to communicate some data through AJAX from the backend to the frontend (Javascript, of course). The XMLHttpRequest is done by a Prototypejs-written AJAX-handler (I'm still in the process of migrating to jQuery, but the noConflict-mode allows me to run both simultaneously), after which PHP sends the X-Json header with some data like objects = {'foo': 'bar'}.
The Prototypejs-Ajax.Request passes a json variable to the onSuccess-callback, containing all the JSONdata, so the processing isn't the hard part. However, I noticed that since the JSON is automatically evaluated, the objects variable is made global (and thus a member of the window object.
I know it's not really a problem since it's just an unused variable, but I always tried to stay away from global variables as much as possible. Additionally, the datasets may be pretty large on some occasions, so it'll just be a huge duplicate variable of which one is never used. So, if possible, I'd rather lose the global.
My question: how? If there's a good reason for this happening, or if this is just a Prototypejs-specific issue, or if this just indicates I'm doing something very wrong, please tell me :-)
Thanks!
Are you sending back objects = {"foo":"bar"} from PHP? When sending JSON, you just send {"foo":"bar"} and get the data as the return result of eval:
var json = '{"foo":"bar"}'; // This string would really be coming from PHP
// ...
var objects = eval('(' + json + ')'); // objects variable will be limited to the
// current scope.
If, for some reason, you must evaluate objects = ..., you can limit the variable to the current scope before running eval:
var objects;
eval('objects = {"foo":"bar"}');
Note that the PHP functions json_encode and json_decode will create "proper" JSON for you, which means it will use double quotes, and it will not use any kind of assignment (True JSON is limited to an array/object as the outer-most value, and it may not contain assignment or function calls. See the JSON specification.)
If the PHP is outputting objects = {'foo': 'bar'} then it isn't outputting JSON. JSON can't include the '=' character outside a string and uses the double quote character not the single quote. This means it is outputting JavaScript.
You have two options here.
The first is to try to restrict the scope of the JavaScript, the second is to fix the PHP so it outputs real JSON.
I would recommend the second option. Deal with JSON not JavaScript.
If you want to try to restrict it, then you need to limit the scope somehow. Possibly with:
function () {
eval("var " + php_generated_code);
doSomethingWith(object);
}
… although I'm not sure what the scope of eval() is.

Categories