Tarayıcıda Sandbox JavaScript Çalıştırmak Mümkün mü?


142

Bir HTML sayfasında çalışan JavaScript kodunda normalde bulunan özelliklere erişimi engellemek için tarayıcıda çalışan JavaScript'i korumanın mümkün olup olmadığını merak ediyorum.

Örneğin, son kullanıcılar için "ilginç olaylar" gerçekleştiğinde olay işleyicilerini çalıştıracaklarını tanımlamaları için bir JavaScript API'si sağlamak istiyorum, ancak bu kullanıcıların windownesnenin özelliklerine ve işlevlerine erişmesini istemiyorum . Bunu yapabilir miyim?

En basit durumda, diyelim ki kullanıcıların aramasını engellemek istiyorum alert. Düşünebileceğim birkaç yaklaşım:

  • window.alertGlobal olarak yeniden tanımlayın . Sayfada çalışan diğer kod (yani kendi olay işleyicilerinde kullanıcılar tarafından yazılan şeyler) kullanmak isteyebilirsiniz, çünkü bu geçerli bir yaklaşım olacağını sanmıyorum alert.
  • Olay işleyici kodunu işlemek için sunucuya gönderin. Olay işleyicileri sayfa bağlamında çalışması gerektiğinden, kodu işlemek için sunucuya göndermek doğru bir yaklaşım olduğundan emin değilim.

Belki de sunucu kullanıcı tanımlı işlevi işler ve daha sonra istemci üzerinde yürütülecek bir geri arama oluşturur bir çözüm işe yarayacak? Bu yaklaşım işe yarıyor olsa bile, bu sorunu çözmenin daha iyi yolları var mı?

Yanıtlar:


54

Google Caja , "güvenilmeyen üçüncü taraf HTML ve JavaScript'i sayfanıza satır içi koymanıza ve yine de güvende kalmanıza olanak tanıyan" kaynaktan kaynağa bir çevirmen.


5
Hızlı bir test, Caja'nın tarayıcıyı CPU saldırılarına karşı koruyamadığını gösterir - while (1) {}sadece askıda kalır. Aynı şekilde a=[]; while (1) { a=[a,a]; }.
David Verilen

5
Evet, hizmet reddi kapsam dışında: code.google.com/p/google-caja/issues/detail?id=1406
Darius Bacon

32

Douglas Crockford'un ADsafe'sine bir göz atın :

ADsafe, herhangi bir web sayfasına konuk kodu (üçüncü taraf komut dosyası reklamları veya widget'ları gibi) koymayı güvenli hale getirir. ADsafe, konuk kodunun değerli etkileşimler gerçekleştirmesine izin verecek kadar güçlü bir JavaScript alt kümesi tanımlar ve aynı zamanda kötü amaçlı veya kazara hasar veya izinsiz girişi önler. ADsafe alt kümesi, JSLint gibi araçlarla mekanik olarak doğrulanabilir, böylece güvenlik için konuk kodunu gözden geçirmek için hiçbir insan denetimi gerekmez. ADsafe alt kümesi, iyi kodlama uygulamalarını da uygulayarak konuk kodunun doğru bir şekilde çalışma olasılığını artırır.

Projenin GitHub deposundakitemplate.html ve template.jsdosyalarına bakarak ADsafe'in nasıl kullanılacağına ilişkin bir örnek görebilirsiniz .


Sitelerinde ADsafe kullanmanın bir yolunu göremiyorum. İndirmenin bir yolu yok, koda bağlantı yok, hiçbir şey yok. ADsafe'i nasıl deneyebilirsiniz?
BT

2
Ayrıca, tamamen kabul edilemez olan herhangi bir erişimi önler this. Kullanmadan iyi bir javascript yazamazsınız this.
BT

4
@BT Tüm projeleri kullanmadan yazdım this. Kötü adlandırılmış parametreden kaçınmak zor değil.
soundly_typed

2
@BT Gerçek dünyadaki projeleri tamamlamanın kabul edilemez olduğunu söylemek saçma olurdu. Ama bu tartışmayı başlattığım için üzgünüm ve geri çekilmeliyim; bu tür şeyler (üzgünüm) tartışmak için yer değil. Daha fazla tartışmak isterseniz twitter'dayım.
soundly_typed

1
@BT (Soruyla alakalı olduğu için devam edeceğim) Başka birinin ortamında kod çalıştırdığınızda kural ve kısıtlamalarla karşılaşacaksınız. Bunu kabul edilemez olarak adlandırmam. Belki "kıçından acı". Ama kabul edilemez. Sonuçta, her kullanımı için this, eşit, eşdeğer bir thisyol yoktur (sonuçta sadece bir parametredir).
soundly_typed

24

Jsandbox adlı bir sandboxing kütüphanesi oluşturdum . Ayrıca, başka türlü elde edemeyeceği korumalı alan kodu verilerini açıkça vermek için bir giriş yöntemine sahiptir.

Aşağıdaki API örneğidir:

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: Bu gerçekten harika görünüyor. Kullanıcının kodunu bu şekilde yürütmek ne kadar güvenlidir?
Konstantin Tarkus

1
Çok güvenli. Github'da güncellenmiş kütüphaneye göz atın .
Eli Gray

1
bu proje hala devam ediyor mu? 2 yıldan beri güncellenmediğini görüyorum ...
Yanick Rochon

Sandbox istiyorsanız, ancak yine de jQuery demek için kod erişimine izin vermek dışında bu gibi, web çalışanları DOM manipülasyon izin vermez gibi bu başarısız olur.
Rahly

Merhaba Eli - harika bir lib için teşekkürler, onu korumayı planlıyor musunuz? Hata kodlama işlevselliği eklemek için bir değişiklik isteğim var - koda hızlıca bakarak mümkün olmalı. Lütfen ne düşündüğünü bilmeme izin ver?
user1514042

8

Bence js.js burada bahsetmeye değer. JavaScript ile yazılmış bir JavaScript yorumlayıcısıdır.

Yerel JS'den yaklaşık 200 kat daha yavaş, ancak doğası onu mükemmel bir sanal alan haline getiriyor. Diğer bir dezavantaj boyutu - neredeyse 600 kb, bazı durumlarda masaüstü bilgisayarlar için kabul edilebilir, ancak mobil cihazlar için kabul edilemez.


7

Diğer yanıtlarda belirtildiği gibi, sanal alanlı iframe'deki kodu (sunucu tarafına göndermeden) hapse atmak ve mesajlarla iletişim kurmak yeterlidir. En çok güvenilmeyen koda bazı API sağlama ihtiyacı nedeniyle oluşturduğum küçük bir kütüphaneye bir göz atmanızı öneririm , tıpkı soruda açıklandığı gibi: belirli işlev grubunu doğrudan sanal alana aktarmak için bir fırsat var güvenilmeyen kod çalışır. Ayrıca, bir kullanıcının sanal alanda gönderdiği kodu çalıştıran bir demo da vardır:

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


4

Tüm tarayıcı satıcıları ve HTML5 belirtimi, korumalı alandaki iframe'lere izin vermek için gerçek bir korumalı alan özelliğine yönelik olarak çalışır - ancak yine de iframe ayrıntı düzeyi ile sınırlıdır.

Genel olarak, herhangi bir düzenli ifade vb., Durma sorununa yozlaştığı için keyfi kullanıcı tarafından sağlanan JavaScript'i güvenli bir şekilde sterilize edemez: - /


2
Durma problemine nasıl yozlaştığını açıklayabilir misiniz?
hdgarrood

2
Durma problemini çözmenin teorik imkansızlığı sadece statik kod analizi için geçerlidir. Kum havuzları, durma sorunuyla başa çıkmak için zaman sınırları uygulamak gibi şeyler yapabilir.
Aviendha

4

@ RyanOHara'nın web çalışanları korumalı alan kodunun tek bir dosyada geliştirilmiş bir sürümü (fazladan eval.jsdosya gerekmez).

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

Dene:

https://jsfiddle.net/kp0cq6yw/

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

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

Çıktı almalıdır 6(Chrome ve Firefox'ta test edilmiştir).


2

Çirkin bir yol ama belki de bu sizin için çalışıyor, tüm küreselleri aldım ve onları sanal alan kapsamında yeniden tanımladım, aynı zamanda anonim bir işlev kullanarak küresel nesneyi alamadıkları için katı modu ekledim.

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
Bundan windowgeri almak önemsiz . sandboxcode('console.log((0,eval)("this"))')
Ry-

Bunu nasıl önleyeceğimizi
alejandro

@alejandro Bunu önlemenin bir yolunu buldunuz mu?
Wilt

1
Uygulamam sadece şunu ekler:function sbx(s,p) {e = eval; eval = function(t){console.log("GOT GOOD")}; sandboxcode(s,p); eval =e}
YoniXw

2
@YoniXw: Umarım hiçbir şey için kullanmazsın. Böyle bir yaklaşım işe yaramayacaktır. (_=>_).constructor('return this')()
Ry-

1

Bağımsız bir Javascript yorumlayıcısının yerleşik tarayıcı uygulamasının kafesli bir sürümünden daha sağlam bir sanal alan sağlama olasılığı daha yüksektir. Ryan zaten js.js'den bahsetti , ancak daha güncel bir proje JS-Interpreter . Docs tercüman için çeşitli işlevler göstermesini nasıl koruyabilirim, ama onun kapsamı aksi çok sınırlıdır.


1

2019 itibariyle, vm2 bu soruna en popüler ve en düzenli olarak güncellenen çözüm gibi görünüyor.


vm2 tarayıcıda çalışma zamanını desteklemez. Ancak, bir nodejs uygulamasında korumalı alan kodunu arıyorsanız çalışmalıdır.
kevin.groat

0

NISP ile korumalı alanda değerlendirme yapabilirsiniz. Yazdığınız ifade tam olarak bir JS olmasa da, s ifadeleri yazacaksınız. Kapsamlı programlama gerektirmeyen basit DSL'ler için idealdir.


-3

1) Çalıştırmak için bir kodunuz olduğunu varsayalım:

var sCode = "alert(document)";

Şimdi, bunu bir sanal alanda yürütmek istediğinizi varsayalım:

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

Yürütüldüğünde bu iki satır başarısız olur, çünkü "sandbox" da "alert" işlevi kullanılamaz

2) Ve şimdi pencere nesnesinin bir üyesini işlevselliğinizle ortaya çıkarmak istiyorsunuz:

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

Gerçekten kaçan tırnaklar ekleyebilir ve diğer parlatma yapabilirsiniz, ancak sanırım fikir açıktır.


7
Küresel nesneye ulaşmak için sayısız başka yol yok mu? Örneğin func.apply (null) işlevini kullanan bir işlev içinde "this" pencere nesnesi olacaktır.
mbarkhau

5
İlk örnek başarısız olmaz, bu çok geçersiz bir sanal alan örneğidir.
Andy E

1
var sCode = "this.alert ('FAIL')";
Leonard Pauli

-4

Bu kullanıcı JavaScript'i nereden geliyor?

Bir kullanıcıyı sayfanıza kod gömme ve ardından tarayıcılarından çağıran hakkında yapabileceğiniz çok şey yoktur (bkz. Greasemonkey, http://www.greasespot.net/ ). Bu sadece tarayıcıların yaptığı bir şey.

Ancak, komut dosyasını bir veritabanında depolarsanız, alın ve eval () yaparsanız, çalıştırılmadan önce komut dosyasını temizleyebilirsiniz.

Tüm pencereleri kaldıran kod örnekleri. ve belge. Referanslar:

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

Bu, aşağıdakilerin yürütülmesini (test edilmedi) engellemeye çalışır:

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

Güvenli olmayan kullanıcı komut dosyasına uygulamanız gereken birçok sınırlama vardır. Ne yazık ki JavaScript için 'korumalı alan kapsayıcısı' yok.


2
Birisi kötü amaçlı bir şey yapmaya çalışıyorsa, basit bir normal ifade bunu yapamaz - take (function () {this ["loca" + "tion"] = " example.com ";}) () kullanıcılarınıza güvenemez (bu, keyfi kişilerin içerik ekleyebileceği herhangi bir sitede olduğu gibi) tüm j'leri engellemek gerekir.
olliej

Geçmişte benzer bir şey kullandım. Mükemmel değil, ama sizi oraya götürür.
Sugendran

olliej, böyle bir tekniğin sınırlamaları konusunda haklısınız. <code> var window = null, document = null, this = {}; </code> gibi global değişkenlerin üzerine yazmaya ne dersiniz?
Dimitry

Dimitry Z, bu değişkenlerin üzerine yazılmasına izin verilmiyor [bazı tarayıcılarda]. Ayrıca cevaplar listesindeki çözümümü kontrol edin - işe yarıyor.
Sergey Ilinsky

-5

Kullanıcıların sitem için uygulama oluşturmasına izin vermek için basit bir js sanal alanı üzerinde çalışıyorum. DOM erişimine izin vermede hala bazı zorluklarla karşılaşmama rağmen (parentNode, işleri güvende tutmama izin vermiyor = /), yaklaşımım sadece pencere nesnesini bazı yararlı / zararsız üyeleriyle yeniden tanımlamak ve daha sonra kullanıcıyı değerlendirmek () Bu yeniden tanımlanmış pencerede varsayılan kapsam olarak kodlayın.

Benim "çekirdek" kodu böyle gider ... (Ben tamamen göstermiyorum;)

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

Yani, bir Sandbox örnek ve kod çalıştırmak için execute () kullanabilirsiniz. Ayrıca, değerlendirilen koddaki tüm yeni bildirilen değişkenler sonuçta execute () kapsamına bağlanır, bu nedenle adların çakışması veya mevcut kodla uğraşması olmaz.

Genel nesneler hala erişilebilir olsa da, korumalı alan kodu tarafından bilinmeyenler Sandbox :: scope nesnesindeki proxy'ler olarak tanımlanmalıdır.

Umarım bu işe yarar.


8
Bu hiçbir şeyi korumaz. Değerlendirilen kod üyeleri silebilir ve küresel kapsama bu şekilde girebilir veya (function () {return this;}) ()
Mike Samuel

-6

Kullanıcının kodunu, yasaklanmış nesneleri parametre olarak yeniden tanımlayan bir işleve sarabilirsiniz - bunlar daha sonra undefinedçağrılır:

(function (alert) {

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

}) ();

Tabii ki, akıllı saldırganlar Javascript DOM'yi inceleyerek ve pencereye başvuru içeren geçersiz kılınmamış bir nesne bularak bu sorunu çözebilirler.


Başka bir fikir, kullanıcının kodunu jslint gibi bir araç kullanarak taramaktır . Önceden ayarlanmış değişken (veya: yalnızca istediğiniz değişkenler) olmayacak şekilde ayarlandığından emin olun ve ardından herhangi bir global ayarlanmış veya erişilmişse kullanıcının komut dosyasının kullanılmasına izin vermeyin. Yine, kullanıcının değişmez anahtarlar kullanarak oluşturabileceği DOM nesnelerini yürümeye karşı savunmasız olabilir, sanal alandan kaçmak için erişilebilen pencere nesnesine örtülü başvurular olabilir.


2
Kullanıcı düz uyarı yerine window.alert girdiyse, bu sınırı atlar.
Quentin

@Borward: evet, dolayısıyla "yasak nesneler". wrunsby, kullanıcının hangi nesnelere erişmesine izin verilmediğine karar vermeli ve bunları parametre listesine yerleştirmelidir.
John Millikin

Sadece bir nesne var - pencere. Erişimi engellemezseniz, her şey ona erişilebilir. Engellerseniz, komut dosyası özelliklerinin hiçbirine erişemez (çünkü window.alert yerine uyarı söylüyorum sadece pencereyi ima eder.).
Quentin

@Doward: window.alert'i bloke edeceğiniz durum bu değil, ancak uyarı yine de çalışır, deneyin. Çünkü pencere aynı zamanda küresel bir nesnedir. Birinin, kullanıcı kodunun erişmesini istemediğiniz pencereyi ve pencerenin herhangi bir özelliğini veya yöntemini engellemesi gerekir.
AnthonyWJones
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.