Создание WebWorkers безопасной среды
в стремлении иметь интерфейс, способный запускать произвольный код javascript внутри браузера, не имея дыры в безопасности размером с типичную шутку yo-mama, Esailija предложила использовать Веб-Работников. Они работают в полу-изолированной среде (без доступа DOM и уже внутри браузера) и могут быть убиты, поэтому пользователь не может поместить их в бесконечный цикл.
вот пример, который он привел: http://tuohiniemi.fi / ~runeli/petka/workertest.html (Откройте консоль)
jsfiddle (только Google chrome)
теперь это кажется хорошим решением; однако, является ли оно полным (или приближается к полному)? Есть ли что-то очевидное упускаю?
все это (как это подключено к боту) можно найти на github:работник, оценщика
main:
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 );
};
2 ответов:
текущий код (перечисленный ниже) уже некоторое время используется в чате StackOverflow javascript и до сих пор самая сложная проблема была
Array(5000000000).join("adasdadadasd")мгновенно сбой какой вкладки браузера для меня, когда я запускал бот-исполнитель кода. MonkeypatchingArray.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) ); }; })();
код в настоящее время (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"к консоли, не проходя через поддельныеconsole.logчто веб-работник предоставляет. Приведенный выше код используетself, который может быть внесен в черный список (удалив его из белого списка), ноthisиglobalтакже работать. Я обнаружил, что попытки внести в черный списокglobalсбой на FF и Chrome: рабочий умирает с помощью ошибка.Примечание: Chrome отказывается от черного списка
Intlпоэтому он должен быть добавлен в белый список для запуска кода вообще.
Comments