브라우저에서 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.html및 template.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.