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


142

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

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

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

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

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

답변:


54

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


5
빠른 테스트는 Caja가 브라우저를 CPU 공격으로부터 보호 할 수 없다는 것을 보여줍니다 while (1) {}. 마찬가지로 a=[]; while (1) { a=[a,a]; }.
David 주어진

5
예, 서비스 거부는 범위를 벗어납니다. code.google.com/p/google-caja/issues/detail?id=1406
Darius Bacon

32

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

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

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


그들의 사이트에서는 ADsafe를 사용하는 방법이 없습니다. 다운로드 할 수있는 방법이 없으며 코드 링크가 없으며 아무것도 없습니다. ADsafe를 어떻게 시험해볼 수 있습니까?
BT

2
또한에 대한 액세스를 차단 하므로this 완전히 허용되지 않습니다. 을 사용하지 않으면 자바 스크립트를 작성할 수 없습니다 this.
BT

4
@BT를 사용하지 않고 전체 프로젝트를 작성했습니다 this. 이름이 잘못된 매개 변수를 피하는 것은 어렵지 않습니다.
soundly_typed

2
@BT 실제 프로젝트를 완료하는 것은 용납 될 수 없다고 말하는 것은 어리석은 일입니다. 그러나 나는이 토론을 시작하는 것을 후회하고 철회해야한다. 이것은 그런 것들을 논의 할 곳이 아닙니다 (죄송합니다). 더 논의하고 싶다면 트위터에 있습니다.
soundly_typed

1
@BT (질문과 관련이 있으므로 계속하겠습니다.) 다른 환경에서 코드를 실행할 때마다 규칙과 제한 사항이 적용됩니다. 나는 그것을 받아 들일 수 없다고 부르지 않을 것이다. 아마도 "엉덩이에 통증"이있을 수 있습니다. 그러나 용납 할 수 없습니다. 결국을 사용할 때마다 this동등한 동등한 this방법이 없습니다 (결국 매개 변수 일뿐입니다).
soundly_typed

24

웹 작업자를 사용하여 평가 된 코드를 샌드 박스로 만드는 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
      }
  });

+1 : 정말 멋지다. 이런 식으로 사용자 코드를 실행하는 것이 얼마나 안전합니까?
콘스탄틴 타르 쿠스

1
매우 안전. github 에서 업데이트 된 라이브러리 확인하십시오 .
Eli Gray

1
이 프로젝트는 여전히 유지되고 있습니까? 2 년이 지난 후에도 업데이트되지 않은 것으로 보입니다 ...
Yanick Rochon

샌드 박스를 만들고 싶지만 jQuery를 말하기 위해 코드 액세스를 허용하려는 경우 웹 작업자가 DOM 조작을 허용하지 않으므로 실패합니다.
Rahly

안녕 엘리-훌륭한 라이브러리에 감사드립니다. 유지 관리 할 계획입니까? 디버깅 기능을 추가하기위한 변경 요청이 있습니다. 코드를 빠르게 살펴보면 가능합니다. 당신이 어떻게 생각하는지 알려주세요?
user1514042

8

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

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


7

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

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


4

모든 브라우저 공급 업체와 HTML5 사양은 샌드 박스 된 iframe을 허용하기 위해 실제 샌드 박스 속성을 향해 노력하고 있지만 여전히 iframe 단위로 제한됩니다.

일반적으로, 정규 표현식 등은 정지 문제로 퇴화함에 따라 임의의 사용자 제공 JavaScript를 안전하게 위생 처리 할 수 ​​없습니다 :-/


2
정지 문제로 어떻게 퇴화되는지 설명 할 수 있습니까?
hdgarrood

2
정지 문제를 해결하는 이론적 불가능 성은 실제로 정적 코드 분석에만 적용됩니다. 샌드 박스는 중지 문제를 처리하기 위해 시간 제한을 적용하는 것과 같은 작업을 수행 할 수 있습니다.
Aviendha

4

@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);
      });

출력해야합니다 6(Chrome 및 Firefox에서 테스트).


2

추악한 방법이지만 아마도 이것이 당신에게 효과적 일 것입니다. 나는 모든 전역을 가져 와서 샌드 박스 범위에서 재정의했습니다. 또한 엄격한 모드를 추가하여 익명 함수를 사용하여 전역 객체를 가져올 수 없습니다.

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


3
window그로부터 돌아 오는 것은 사소한 일 입니다. sandboxcode('console.log((0,eval)("this"))')
Ry-

이를 방지하는 방법을 알아
내야

@alejandro 그것을 막을 방법을 찾았습니까?
Wilt December

1
내 구현은 다음을 추가합니다.function sbx(s,p) {e = eval; eval = function(t){console.log("GOT GOOD")}; sandboxcode(s,p); eval =e}
YoniXw

2
@ YoniXw : 나는 당신이 아무것도 그것을 사용하지 않기를 바랍니다. 이와 같은 접근 방식은 작동하지 않습니다. (_=>_).constructor('return this')()
Ry-

1

독립 Javascript 인터프리터는 내장 브라우저 구현의 갇힌 버전보다 강력한 샌드 박스를 생성 할 가능성이 높습니다. Ryan은 이미 js.js 에 대해 언급 했지만 최신 프로젝트는 JS-Interpreter 입니다. 이 문서 는 다양한 기능을 인터프리터에 노출시키는 방법을 다루지 만 그 범위는 매우 제한적입니다.


1

2019 년 현재 vm2 는이 문제에 대한 가장 인기 있고 정기적으로 업데이트되는 솔루션처럼 보입니다.


vm2는 브라우저에서 런타임을 지원하지 않습니다. 그러나 nodejs 앱에서 코드를 샌드 박스로 보려는 경우 작동해야합니다.
kevin.groat

0

NISP 를 사용하면 샌드 박스 평가를 수행 할 수 있습니다. 작성하는 표현식이 정확히 JS는 아니지만 대신 s-expressions를 작성합니다. 광범위한 프로그래밍이 필요없는 간단한 DSL에 이상적입니다.


-3

1) 실행할 코드가 있다고 가정하십시오.

var sCode = "alert(document)";

이제 샌드 박스에서 실행한다고 가정합니다.

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

"sandbox"에서 "alert"기능을 사용할 수 없기 때문에이 두 줄이 실행될 때 실패합니다.

2) 이제 기능으로 window 객체의 멤버를 노출하려고합니다.

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

실제로 이스케이프 따옴표를 추가하고 다른 연마를 할 수는 있지만 아이디어는 분명합니다.


7
전역 객체를 얻는 다른 방법이 없습니까? 예를 들어 func.apply (null) "this"를 사용하여 호출 된 함수 내에서이 객체는 윈도우 객체가됩니다.
mbarkhau

5
첫 번째 예는 실패하지 않습니다. 이것은 매우 잘못된 샌드 박싱 예입니다.
Andy E

1
var sCode = "this.alert ( 'FAIL')";
레너드 파울리

-4

이 사용자 JavaScript는 어디에서 왔습니까?

사용자가 코드를 페이지에 임베드 한 다음 브라우저에서 코드를 호출하는 방법은 그리 많지 않습니다 (Greasemonkey, http://www.greasespot.net/ 참조) . ). 브라우저가하는 일입니다.

그러나 스크립트를 데이터베이스에 저장 한 다음 검색하고 eval ()하면 스크립트를 실행하기 전에 정리할 수 있습니다.

모든 창을 제거하는 코드의 예. 그리고 문서. 참고 문헌 :

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

이렇게하면 다음이 실행되지 않습니다 (테스트되지 않음).

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

안전하지 않은 사용자 스크립트에 적용해야하는 많은 제한이 있습니다. 불행히도 JavaScript에는 '샌드 박스 컨테이너'가 없습니다.


2
누군가가 악의적 인 일을 시도하는 경우 간단한 정규 표현식으로 할 수 없습니다.-(function () {this [ "loca"+ "tion"] = " example.com ";}) () 일반적으로 모든 js를 차단하는 사용자 (임의의 사람들이 콘텐츠를 추가 할 수있는 사이트의 경우)를 신뢰할 수 없습니다.
olliej

나는 과거에 비슷한 것을 사용했습니다. 완벽하지는 않지만 대부분의 방법을 제공합니다.
Sugendran

olliej, 당신은 그러한 기술의 한계에 대해 옳습니다. <code> var window = null, document = null, this = {}; </ code>와 같은 전역 변수를 덮어 쓰는 것은 어떻습니까?
Dimitry

Dimitry Z, 이러한 변수를 덮어 쓰는 것은 [일부 브라우저에서] 허용되지 않습니다. 또한 답변 목록에서 내 솔루션을 확인하십시오-작동합니다.
Sergey Ilinsky

-5

사용자가 내 사이트의 애플릿을 빌드 할 수 있도록하기 위해 단순한 js 샌드 박스를 작업하고 있습니다. DOM 액세스를 허용하는 데 여전히 어려움이 있지만 (parentNode는 보안을 유지하지 못합니다 = /), 내 접근 방식은 유용하고 무해한 멤버로 창 객체를 재정의 한 다음 사용자를 평가하는 것입니다. 이 재정의 된 창을 기본 범위로 사용하는 코드

내 "핵심"코드는 다음과 같습니다 ... (전적으로 표시하지는 않습니다.)

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);
            }
        }
    };
}

따라서 Sandbox를 인스턴스화하고 execute ()를 사용하여 코드를 실행할 수 있습니다. 또한 평가 된 코드 내에서 새로 선언 된 모든 변수는 결국 execute () 범위에 바인딩되므로 이름이 충돌하거나 기존 코드와 혼동되지 않습니다.

전역 객체는 여전히 접근 가능하지만 샌드 박스 코드에 알려지지 않은 객체는 Sandbox :: scope 객체에서 프록시로 정의해야합니다.

이것이 효과가 있기를 바랍니다.


8
이것은 아무것도 샌드 박스하지 않습니다. 회피 된 코드는 멤버를 삭제하고 그런 식으로 전역 범위에 도달하거나 (function () {return this;}) ()를 수행하여 ghe 전역 범위에 대한 참조를 가져올 수 있습니다.
Mike Samuel

-6

금지 된 객체를 매개 변수로 재정의하는 함수로 사용자 코드를 래핑 할 수 있습니다 undefined.

(function (alert) {

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

}) ();

물론 현명한 공격자는 Javascript DOM을 검사하고 창에 대한 참조가 포함 된 재정의되지 않은 개체를 찾아이 문제를 해결할 수 있습니다.


또 다른 아이디어는 jslint 와 같은 도구를 사용하여 사용자 코드를 스캔하는 것 입니다 . 사전 설정 변수가 없도록 설정되어 있는지 확인하십시오 (또는 원하는 변수 만). 글로벌이 설정되거나 액세스되면 사용자의 스크립트를 사용하지 마십시오. 다시 말하지만 DOM을 걷는 데 취약 할 수 있습니다. 사용자가 리터럴을 사용하여 구성 할 수있는 객체는 샌드 박스를 벗어나기 위해 액세스 할 수있는 윈도우 객체에 대한 암시 적 참조를 가질 수 있습니다.


2
사용자가 일반 경보 대신 window.alert를 입력하면 해당 한계를 무시합니다.
Quentin

@Dorward : 예. 따라서 "금지 된 개체"입니다. wrunsby는 사용자가 액세스 할 수없는 오브젝트를 결정하여 매개 변수 목록에 배치해야합니다.
John Millikin

하나의 개체가 있습니다-창. 액세스를 차단하지 않으면 모든 것을 사용할 수 있습니다. 차단하면 스크립트는 속성에 액세스 할 수 없습니다 (window.alert 대신 alert라고 말하면 창을 의미하기 때문에).
Quentin

@Doward : window.alert를 차단하는 경우는 아니지만 경고가 여전히 작동합니다. 시도하십시오. window도 전역 개체이기 때문입니다. 사용자 코드가 액세스하기를 원하지 않는 창과 속성 또는 창 방법을 차단해야합니다.
AnthonyWJones
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.