development

브라우저에서 JavaScript를 샌드 박스로 실행할 수 있습니까?

big-blog 2020. 6. 26. 07:46
반응형

브라우저에서 JavaScript를 샌드 박스로 실행할 수 있습니까?


HTML 페이지에서 실행되는 JavaScript 코드에 일반적으로 사용 가능한 기능에 액세스하지 못하도록 브라우저에서 실행중인 JavaScript를 샌드 박싱 할 수 있는지 궁금합니다.

예를 들어, "관심있는 이벤트"가 발생할 때 실행되도록 이벤트 처리기를 정의 할 수 있도록 최종 사용자에게 JavaScript API를 제공하려고하지만 해당 사용자가 window개체 의 속성과 기능에 액세스하는 것을 원하지 않습니다 . 내가 할 수 있습니까?

가장 간단한 경우, 사용자가 전화를 걸지 못하게한다고 가정하겠습니다 alert. 내가 생각할 수있는 몇 가지 접근 방식은 다음과 같습니다.

  • window.alert세계적으로 재정의하십시오 . 페이지에서 실행중인 다른 코드 (즉, 이벤트 처리기에서 사용자가 작성하지 않은 것)를 사용하기를 원하기 때문에 이것이 유효한 접근 방법이라고 생각하지 않습니다 alert.
  • 처리 할 서버로 이벤트 핸들러 코드를 보냅니다. 이벤트 처리기가 페이지의 컨텍스트에서 실행되어야하기 때문에 처리 할 서버로 코드를 보내는 것이 올바른 방법인지 확실하지 않습니다.

아마도 서버가 사용자 정의 함수를 처리 한 다음 클라이언트에서 실행할 콜백을 생성하는 솔루션이 작동합니까? 이 방법이 효과가 있다고해도이 문제를 해결하는 더 좋은 방법이 있습니까?


Google Caja 는 "신뢰할 수없는 타사 HTML 및 JavaScript를 페이지에 인라인으로 배치하고 안전하게 유지할 수있는 소스 간 번역기"입니다.


Douglas Crockford의 ADsafe를 살펴보십시오 .

ADsafe를 사용하면 모든 웹 페이지에 게스트 코드 (예 : 타사 스크립트 광고 또는 위젯)를 안전하게 배치 할 수 있습니다. ADsafe는 게스트 코드가 귀중한 상호 작용을 수행하는 동시에 악의적이거나 우발적 인 손상이나 침입을 방지 할 수있을 정도로 강력한 JavaScript의 하위 집합을 정의합니다. ADsafe 하위 집합은 JSLint와 같은 도구를 통해 기계적으로 확인할 수 있으므로 안전을 위해 게스트 코드를 검토하기 위해 사람의 검사가 필요하지 않습니다. ADsafe 하위 집합은 또한 우수한 코딩 방법을 적용하여 게스트 코드가 올바르게 실행될 가능성을 높입니다.

프로젝트의 GitHub 리포지토리 에서 template.htmltemplate.js파일 을 보면 ADsafe를 사용하는 방법의 예를 볼 수 있습니다 .


웹 작업자를 사용하여 평가 된 코드를 샌드 박스로 만드는 jsandbox 라는 샌드 박스 라이브러리를 만들었습니다 . 또한 샌드 박스 코드 데이터를 명시 적으로 제공하기위한 입력 방법이 있습니다.

다음은 API의 예입니다.

jsandbox
    .eval({
      code    : "x=1;Math.round(Math.pow(input, ++x))",
      input   : 36.565010597564445,
      callback: function(n) {
          console.log("number: ", n); // number: 1337
      }
  }).eval({
      code   : "][];.]\\ (*# ($(! ~",
      onerror: function(ex) {
          console.log("syntax error: ", ex); // syntax error: [error object]
      }
  }).eval({
      code    : '"foo"+input',
      input   : "bar",
      callback: function(str) {
          console.log("string: ", str); // string: foobar
      }
  }).eval({
      code    : "({q:1, w:2})",
      callback: function(obj) {
          console.log("object: ", obj); // object: object q=1 w=2
      }
  }).eval({
      code    : "[1, 2, 3].concat(input)",
      input   : [4, 5, 6],
      callback: function(arr) {
          console.log("array: ", arr); // array: [1, 2, 3, 4, 5, 6]
      }
  }).eval({
      code    : "function x(z){this.y=z;};new x(input)",
      input   : 4,
      callback: function(x) {
          console.log("new x: ", x); // new x: object y=4
      }
  });

js.js 는 여기서 언급 할 가치가 있다고 생각합니다 . JavaScript로 작성된 JavaScript 인터프리터입니다.

기본 JS보다 약 200 배 느리지 만 특성상 완벽한 샌드 박스 환경을 만듭니다. 또 다른 단점은 크기가 거의 600kb로, 경우에 따라 데스크톱에는 적합하지만 모바일 장치에는 적합하지 않을 수 있습니다.


다른 응답에서 언급했듯이 코드를 샌드 박스로 된 iframe에서 (서버 측으로 보내지 않고) 감옥에 보내고 메시지와 통신하면 충분합니다. 질문에 설명 된 것처럼 신뢰할 수없는 코드에 API를 제공해야하기 때문에 주로 만든 작은 라이브러리를 살펴 보는 것이 좋습니다 . 특정 함수 집합을 샌드 박스로 바로 내보낼 수있는 기회가 있습니다. 신뢰할 수없는 코드가 실행됩니다. 그리고 샌드 박스에서 사용자가 제출 한 코드를 실행하는 데모도 있습니다.

http://asvd.github.io/jailed/demos/web/console/


@RyanOHara 웹 워커 샌드 박스 코드의 개선 된 버전을 단일 파일로 제공합니다 (추가 eval.js파일이 필요 하지 않음 ).

function safeEval(untrustedCode)
    {
    return new Promise(function (resolve, reject)
    {

    var blobURL = URL.createObjectURL(new Blob([
        "(",
        function ()
            {
            var _postMessage = postMessage;
            var _addEventListener = addEventListener;

            (function (obj)
                {
                "use strict";

                var current = obj;
                var keepProperties = [
                    // required
                    'Object', 'Function', 'Infinity', 'NaN', 'undefined', 'caches', 'TEMPORARY', 'PERSISTENT', 
                    // optional, but trivial to get back
                    'Array', 'Boolean', 'Number', 'String', 'Symbol',
                    // optional
                    'Map', 'Math', 'Set',
                ];

                do {
                    Object.getOwnPropertyNames(current).forEach(function (name) {
                        if (keepProperties.indexOf(name) === -1) {
                            delete current[name];
                        }
                    });

                    current = Object.getPrototypeOf(current);
                }
                while (current !== Object.prototype);
                })(this);

            _addEventListener("message", function (e)
            {
            var f = new Function("", "return (" + e.data + "\n);");
            _postMessage(f());
            });
            }.toString(),
        ")()"], {type: "application/javascript"}));

    var worker = new Worker(blobURL);

    URL.revokeObjectURL(blobURL);

    worker.onmessage = function (evt)
        {
        worker.terminate();
        resolve(evt.data);
        };

    worker.onerror = function (evt)
        {
        reject(new Error(evt.message));
        };

    worker.postMessage(untrustedCode);

    setTimeout(function () {
        worker.terminate();
        reject(new Error('The worker timed out.'));
        }, 1000);
    });
    }

그것을 테스트하십시오 :

https://jsfiddle.net/kp0cq6yw/

var promise = safeEval("1+2+3");

promise.then(function (result) {
      alert(result);
      });

It should output 6 (tested in Chrome and Firefox).


All the browser vendors and the HTML5 specification are working towards an actual sandbox property to allow sandboxed iframes -- but it's still limited to iframe granularity.

In general, no degree of regular expressions, etc. can safely sanitise arbitrary user provided JavaScript as it degenerates to the halting problem :-/


An ugly way but maybe this works for you , I took all the globals and redefined them in the sandbox scope , as well I added the strict mode so they can't get the global object using an anonymous function.

function construct(constructor, args) {
  function F() {
      return constructor.apply(this, args);
  }
  F.prototype = constructor.prototype;
  return new F();
}
// Sanboxer 
function sandboxcode(string, inject) {
  "use strict";
  var globals = [];
  for (var i in window) {
    // <--REMOVE THIS CONDITION
    if (i != "console")
    // REMOVE THIS CONDITION -->
    globals.push(i);
  }
  globals.push('"use strict";\n'+string);
  return construct(Function, globals).apply(inject ? inject : {});
}
sandboxcode('console.log( this, window, top , self, parent, this["jQuery"], (function(){return this;}()));'); 
// => Object {} undefined undefined undefined undefined undefined undefined 
console.log("return of this", sandboxcode('return this;', {window:"sanboxed code"})); 
// => Object {window: "sanboxed code"}

https://gist.github.com/alejandrolechuga/9381781


An independent Javascript interpreter is more likely to yield a robust sandbox than a caged version of the builtin browser implementation. Ryan has already mentioned js.js, but a more up-to-date project is JS-Interpreter. The docs cover how to expose various functions to the interpreter, but its scope is otherwise very limited.


As of 2019, vm2 looks like the most popular and most regularly-updated solution to this problem.


1) Suppose you have a code to execute:

var sCode = "alert(document)";

Now, suppose you want to execute it in a sandbox:

new Function("window", "with(window){" + sCode + "}")({});

These two lines when executed will fail, because "alert" function is not available from the "sandbox"

2) And now you want to expose a member of window object with your functionality:

new Function("window", "with(window){" + sCode + "}")({
    'alert':function(sString){document.title = sString}
});

Indeed you can add quotes escaping and make other polishing, but I guess the idea is clear.


Where is this user JavaScript coming from?

There is not much you can do about a user embedding code into your page and then calling it from their browser (see Greasemonkey, http://www.greasespot.net/). It's just something browsers do.

However, if you store the script in a database, then retrieve it and eval() it, then you can clean up the script before it is run.

Examples of code that removes all window. and document. references:

 eval(
  unsafeUserScript
    .replace(/\/\/.+\n|\/\*.*\*\/, '') // Clear all comments
    .replace(/\s(window|document)\s*[\;\)\.]/, '') // removes window. or window; or window)
 )

This tries to prevent the following from being executed (not tested):

window.location = 'http://mydomain.com';
var w = window  ;

There are a lot of limitations you would have to apply to the unsafe user script. Unfortunately, there is no 'sandbox container' available for JavaScript.


I've been working on a simplistic js sandbox for letting users build applets for my site. Although I still face some challenges with allowing DOM access (parentNode just won't let me keep things secure =/), my approach was just to redefine the window object with some of its useful/harmless members, and then eval() the user code with this redefined window as the default scope.

My "core" code goes like this... (I'm not showing it entirely ;)

function Sandbox(parent){

    this.scope = {
        window: {
            alert: function(str){
                alert("Overriden Alert: " + str);
            },
            prompt: function(message, defaultValue){
                return prompt("Overriden Prompt:" + message, defaultValue);
            },
            document: null,
            .
            .
            .
            .
        }
    };

    this.execute = function(codestring){

        // here some code sanitizing, please

        with (this.scope) {
            with (window) {
                eval(codestring);
            }
        }
    };
}

So, I can instance a Sandbox and use its execute() to get code running. Also, all new declared variables within eval'd code will ultimately bound to the execute() scope, so there will not be clashing names or messing with existing code.

Although global objects will still be accesible, those which should remain unknown to the sandboxed code must be defined as proxies in the Sandbox::scope object.

Hope this works for you.


You can wrap the user's code in a function that redefines forbidden objects as parameters -- these would then be undefined when called:

(function (alert) {

alert ("uh oh!"); // User code

}) ();

Of course, clever attackers can get around this by inspecting the Javascript DOM and finding a non-overridden object that contains a reference to the window.


Another idea is scanning the user's code using a tool like jslint. Make sure it's set to have no preset variables (or: only variables you want), and then if any globals are set or accessed do not let the user's script be used. Again, might be vulnerable to walking the DOM -- objects that the user can construct using literals might have implicit references to the window object that could be accessed to escape the sandbox.

참고URL : https://stackoverflow.com/questions/195149/is-it-possible-to-sandbox-javascript-running-in-the-browser

반응형