In dem Bestreben, eine Schnittstelle zu haben, die beliebigen Javascript-Code im Browser ausführen kann, ohne eine Sicherheitslücke von der Größe eines typischen Jo-Mama-Witzes zu haben, schlug Esailija die Verwendung von Web Workers vor . Sie werden in einer Semi-Sandbox-Umgebung ausgeführt (kein DOM-Zugriff und bereits im Browser) und können getötet werden, sodass der Benutzer sie nicht in eine Endlosschleife stellen kann.
Hier ist das Beispiel, das er angesprochen hat: http://tuohiniemi.fi/~runeli/petka/workertest.html (öffnen Sie Ihre Konsole)
jsfiddle (nur Google Chrome)
Dies scheint eine gute Lösung zu sein. Ist es jedoch vollständig (oder fast vollständig)? Fehlt etwas Offensichtliches?
Das Ganze (da es an einen Bot angeschlossen ist) ist auf github zu finden: Arbeiter , Bewerter
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 );
});
Arbeiter:
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 );
};
quelle
null
delete
ein natives / Host-Objekt sind, wird es anscheinend in seinem ursprünglichen Zustand wiederhergestellt."delete XMLHttpRequest; XMLHttpRequest;"
Gibt das ursprüngliche XMLHttpRequest-Objekt zurück. Es muss einen Weg geben, dies zuAntworten:
Der aktuelle Code (unten aufgeführt) wird nun seit einiger Zeit im Javascript-Chatroom von Stackoverflow verwendet. Bisher war das größte Problem das
Array(5000000000).join("adasdadadasd")
sofortige Abstürzen einiger Browser-Registerkarten für mich, als ich den Code Executor-Bot ausführte. MonkeypatchingArray.prototype.join
scheint dies behoben zu haben und die maximale Ausführungszeit von 50 ms hat für jeden anderen Versuch funktioniert, den Speicher zu belasten oder den Browser zum Absturz zu bringen.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) ); }; })();
quelle
Object.defineProperty
würde werfen und verhindern, dass deronmessage
Handler eingerichtet wird.Der Code, der derzeit (07.11.2014) in der Frage angezeigt wird, obwohl er angeblich den Zugriff auf
XMLHttpRequest
nicht zulässt (da er nicht auf der Whitelist steht), ermöglicht dem Code weiterhin den Zugriff darauf.Wenn ich den Code in die Frage (oder die akzeptierte Antwort) in eine Kombination aus Webseite und Worker eingefügt und den folgenden Code in Chrome 38 ausgeführt habe:
makeWorkerExecuteSomeCode('event.target.XMLHttpRequest', function (answer) { console.log( answer ); });
Das Ergebnis ist:
function XMLHttpRequest() { [native code] }
Es funktioniert jedoch nicht in FF. Fehler in Chrome?
Eine andere Sache, die ich gefunden habe, die aber nicht sehr weit in das Kaninchenloch zu führen scheint, ist die Wiedereinsetzung
console.log
. Dies funktioniert auf FF 31, aber nicht auf Chrome 38:makeWorkerExecuteSomeCode( 'var c = self.__proto__.__proto__.__lookupGetter__("console").call(self); c.log("FOO");', function (answer) { console.log(answer) });
Dies würde sich
"FOO"
an der Konsole anmelden , ohne dieconsole.log
vom Web-Worker bereitgestellte Fälschung zu durchlaufen . Der obige Code Anwendungenself
, die schwarze Liste gesetzt werden kann (indem sie sie von der weißen Liste zu entfernen) , sondernthis
undglobal
auch Arbeit. Ich habe festgestellt, dass Versuche, eine schwarze Listeglobal
auf FF und Chrome zu erstellen, fehlschlagen: Der Worker stirbt mit einem Fehler.Hinweis: Chrome weigert sich, eine schwarze Liste
Intl
zu erstellen, daher muss sie zur Whitelist hinzugefügt werden, damit der Code überhaupt ausgeführt werden kann.quelle
try/catch
inonmessage
und neu zu definierenevent
mitvar event;
im Verschluss.