在寻求一个能够在浏览器中运行任意javascript代码的界面的过程中,没有一个典型的溜妈妈笑话大小的安全漏洞,Esailija建议使用Web Workers。它们在半沙盒环境中运行(没有DOM访问并且已经在浏览器中)并且可以被杀死,因此用户无法将它们置于无限循环中。
以下是他提出的例子:http://tuohiniemi.fi/~runeli/petka/workertest.html(打开你的控制台)
jsfiddle(仅限Google Chrome)
现在,这似乎是一个很好的解决方案;然而,它是完整的(或接近完整的)吗?有什么明显缺失的吗?
整个事情(因为它连接到机器人)可以在github上找到:worker,evaluator
主:
workercode = "worker.js";
function makeWorkerExecuteSomeCode( code, callback ) {
var timeout;
code = code + "";
var worker = new Worker( workercode );
worker.addEventListener( "message", function(event) {
clearTimeout(timeout);
callback( event.data );
});
worker.postMessage({
code: code
});
timeout = window.setTimeout( function() {
callback( "Maximum execution time exceeded" );
worker.terminate();
}, 1000 );
}
makeWorkerExecuteSomeCode( '5 + 5', function(answer){
console.log( answer );
});
makeWorkerExecuteSomeCode( 'while(true);', function(answer){
console.log( answer );
});
var kertoma = 'function kertoma(n){return n === 1 ? 1 : n * kertoma(n-1)}; kertoma(15);';
makeWorkerExecuteSomeCode( kertoma, function(answer){
console.log( answer );
});
工人:
var global = this;
/* Could possibly create some helper functions here so they are always available when executing code in chat?*/
/* Most extra functions could be possibly unsafe */
var wl = {
"self": 1,
"onmessage": 1,
"postMessage": 1,
"global": 1,
"wl": 1,
"eval": 1,
"Array": 1,
"Boolean": 1,
"Date": 1,
"Function": 1,
"Number" : 1,
"Object": 1,
"RegExp": 1,
"String": 1,
"Error": 1,
"EvalError": 1,
"RangeError": 1,
"ReferenceError": 1,
"SyntaxError": 1,
"TypeError": 1,
"URIError": 1,
"decodeURI": 1,
"decodeURIComponent": 1,
"encodeURI": 1,
"encodeURIComponent": 1,
"isFinite": 1,
"isNaN": 1,
"parseFloat": 1,
"parseInt": 1,
"Infinity": 1,
"JSON": 1,
"Math": 1,
"NaN": 1,
"undefined": 1
};
Object.getOwnPropertyNames( global ).forEach( function( prop ) {
if( !wl.hasOwnProperty( prop ) ) {
Object.defineProperty( global, prop, {
get : function() {
throw new Error( "Security Exception: cannot access "+prop);
return 1;
},
configurable : false
});
}
});
Object.getOwnPropertyNames( global.__proto__ ).forEach( function( prop ) {
if( !wl.hasOwnProperty( prop ) ) {
Object.defineProperty( global.__proto__, prop, {
get : function() {
throw new Error( "Security Exception: cannot access "+prop);
return 1;
},
configurable : false
});
}
});
onmessage = function( event ) {
"use strict";
var code = event.data.code;
var result;
try {
result = eval( '"use strict";\n'+code );
}
catch(e){
result = e.toString();
}
postMessage( "(" + typeof result + ")" + " " + result );
};
答案 0 :(得分:32)
当前代码(下面列出)已经在Stackoverflow javascript聊天室中使用了一段时间
到目前为止,最棘手的问题是Array(5000000000).join("adasdadadasd")
立刻崩溃了一些
我运行代码执行器机器人时的浏览器选项卡。 Monkeypatching Array.prototype.join
似乎已经修复了这个和最大值
50ms的执行时间适用于任何其他企图占用内存或崩溃浏览器的行为。
var global = this;
/* Could possibly create some helper functions here so they are always available when executing code in chat?*/
/* Most extra functions could be possibly unsafe */
var wl = {
"self": 1,
"onmessage": 1,
"postMessage": 1,
"global": 1,
"wl": 1,
"eval": 1,
"Array": 1,
"Boolean": 1,
"Date": 1,
"Function": 1,
"Number" : 1,
"Object": 1,
"RegExp": 1,
"String": 1,
"Error": 1,
"EvalError": 1,
"RangeError": 1,
"ReferenceError": 1,
"SyntaxError": 1,
"TypeError": 1,
"URIError": 1,
"decodeURI": 1,
"decodeURIComponent": 1,
"encodeURI": 1,
"encodeURIComponent": 1,
"isFinite": 1,
"isNaN": 1,
"parseFloat": 1,
"parseInt": 1,
"Infinity": 1,
"JSON": 1,
"Math": 1,
"NaN": 1,
"undefined": 1
};
Object.getOwnPropertyNames( global ).forEach( function( prop ) {
if( !wl.hasOwnProperty( prop ) ) {
Object.defineProperty( global, prop, {
get : function() {
throw "Security Exception: cannot access "+prop;
return 1;
},
configurable : false
});
}
});
Object.getOwnPropertyNames( global.__proto__ ).forEach( function( prop ) {
if( !wl.hasOwnProperty( prop ) ) {
Object.defineProperty( global.__proto__, prop, {
get : function() {
throw "Security Exception: cannot access "+prop;
return 1;
},
configurable : false
});
}
});
Object.defineProperty( Array.prototype, "join", {
writable: false,
configurable: false,
enumerable: false,
value: function(old){
return function(arg){
if( this.length > 500 || (arg && arg.length > 500 ) ) {
throw "Exception: too many items";
}
return old.apply( this, arguments );
};
}(Array.prototype.join)
});
(function(){
var cvalues = [];
var console = {
log: function(){
cvalues = cvalues.concat( [].slice.call( arguments ) );
}
};
function objToResult( obj ) {
var result = obj;
switch( typeof result ) {
case "string":
return '"' + result + '"';
break;
case "number":
case "boolean":
case "undefined":
case "null":
case "function":
return result + "";
break;
case "object":
if( !result ) {
return "null";
}
else if( result.constructor === Object || result.constructor === Array ) {
var type = ({}).toString.call( result );
var stringified;
try {
stringified = JSON.stringify(result);
}
catch(e) {
return ""+e;
}
return type + " " + stringified;
}
else {
return ({}).toString.call( result );
}
break;
}
}
onmessage = function( event ) {
"use strict";
var code = event.data.code;
var result;
try {
result = eval( '"use strict";\n'+code );
}
catch(e) {
postMessage( e.toString() );
return;
}
result = objToResult( result );
if( cvalues && cvalues.length ) {
result = result + cvalues.map( function( value, index ) {
return "Console log "+(index+1)+":" + objToResult(value);
}).join(" ");
}
postMessage( (""+result).substr(0,400) );
};
})();
答案 1 :(得分:5)
该问题中显示的代码(2014-11-07)尽管表面上不允许访问XMLHttpRequest
(因为它未列入白名单),但仍然允许代码访问它。
如果我将问题(或接受的答案)中的代码放在网页和工作人员组合中并在Chrome 38上执行以下代码:
makeWorkerExecuteSomeCode('event.target.XMLHttpRequest', function (answer) { console.log( answer ); });
结果是:
function XMLHttpRequest() { [native code] }
然而它在FF中不起作用。 Chrome中的错误?
我发现的另一件事,但似乎并没有导致rabit洞走得太远,正在恢复console.log
。这适用于FF 31但不适用于Chrome 38:
makeWorkerExecuteSomeCode(
'var c = self.__proto__.__proto__.__lookupGetter__("console").call(self); c.log("FOO");',
function (answer) { console.log(answer) });
这会将"FOO"
记录到控制台,而不会通过Web worker提供的假console.log
。上面的代码使用self
,可以将其列入黑名单(将其从白名单中删除),但this
和global
也可以使用。我发现尝试黑名单global
在FF和Chrome上失败:工作人员因错误而死亡。
注意:Chrome拒绝将Intl
列入黑名单,因此必须将其添加到白名单中才能运行代码。