IT박스

WebWorkers를 안전한 환경으로 만들기

itboxs 2020. 11. 22. 19:17
반응형

WebWorkers를 안전한 환경으로 만들기


Esailija 는 일반적인 yo-mama 농담만큼의 보안 구멍없이 브라우저 내에서 임의의 자바 스크립트 코드를 실행할 수있는 인터페이스를 갖기 위해 Web Workers 사용을 제안했습니다 . 세미 샌드 박스 환경 (DOM 액세스가없고 이미 브라우저 내부에 있음)에서 실행되며 사용자가 무한 루프에 넣을 수 없도록 죽일 수 있습니다.

그가 가져온 예는 다음과 같습니다. http://tuohiniemi.fi/~runeli/petka/workertest.html (콘솔 열기)

jsfiddle (Google 크롬 만 해당)

이제 이것은 좋은 해결책처럼 보입니다. 그러나 완전한 (또는 거의 다가오는) 것입니까? 명백한 누락이 있습니까?

봇에 연결되어 있기 때문에 전체 내용은 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 );
};

현재 코드 (아래에 나열 됨)는 현재 Stackoverflow 자바 스크립트 채팅방에서 잠시 동안 사용되었으며 지금까지 가장 어려운 문제는 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) );
    };

})();

현재 질문에 표시된 코드 (2014-11-07)는 표면 상 액세스를 허용하지 않음에도 불구하고 XMLHttpRequest(허용 목록에 포함되지 않았기 때문에) 여전히 코드에 액세스 할 수 있도록 허용합니다.

If I put the code in the question (or the accepted answer) in a web page and worker combo and execute the following code on Chrome 38:

makeWorkerExecuteSomeCode('event.target.XMLHttpRequest', function (answer) { console.log( answer ); });

The result is:

function XMLHttpRequest() { [native code] } 

However it does not work in FF. Bug in Chrome?

Another thing I found but which does not seem to lead very far down the rabit hole is reinstating console.log. This works on FF 31 but not Chrome 38:

makeWorkerExecuteSomeCode(
    'var c = self.__proto__.__proto__.__lookupGetter__("console").call(self); c.log("FOO");', 
    function (answer) { console.log(answer) });

This would log "FOO" to the console without passing through the fake console.log that the web worker provides. The code above uses self, which can be blacklisted (by removing it from the whitelist) but this and global also work. I've found that attempts to blacklist global fail on FF and Chrome: the worker dies with an error.

Note: Chrome refuses to blacklist Intl so it has to be added to the whitelist for the code to run at all.

참고URL : https://stackoverflow.com/questions/10653809/making-webworkers-a-safe-environment

반응형