É possível colocar o JavaScript em execução no navegador?

estou a perguntar-me se é possível usar o JavaScript da sandbox a correr no navegador para impedir o acesso a funcionalidades que estão normalmente disponíveis para o código JavaScript a correr numa página HTML.

por exemplo, digamos que eu quero fornecer uma API JavaScript para os usuários finais para deixá-los definir os manipuladores de eventos a serem executados quando "eventos interessantes" acontecem, mas eu não quero que esses usuários acessem as propriedades e funções do objeto {[[0]}. Sou capaz de fazer isto?

no caso mais simples, digamos que quero evitar que os utilizadores liguem para alert. Um par de abordagens que eu posso pensar são:

  • Redefine window.alert globalmente. Eu não acho que esta seria uma abordagem válida porque outro código que corre na página (ou seja, coisas que não são de autoria de usuários em seus manipuladores de Eventos) pode querer usar alert.
  • envia o código de tratamento de eventos para o servidor processar. Não tenho a certeza se enviar o código para o servidor para processar é a abordagem certa, porque os responsáveis pelo evento precisam de correr no contexto da página.

talvez uma solução onde o servidor processa a função definida pelo utilizador e, em seguida, gera uma chamada a ser executada no cliente possa funcionar? Mesmo que essa abordagem funcione, existem melhores formas de resolver este problema?

Author: Walter Rumsby, 2008-10-12

14 answers

O Google Caja é um tradutor fonte-a-fonte que "permite que você coloque HTML e JavaScript não confiáveis em linha em sua página e ainda esteja seguro."

 52
Author: Darius Bacon, 2018-04-03 10:59:44
Olha para o ADsafe de Douglas Crockford.:

A ADsafe torna seguro colocar o código de convidado (como publicidade com script de terceiros ou widgets) em qualquer página web. ADsafe define um subconjunto de JavaScript que é poderoso o suficiente para permitir que o código convidado para realizar interações valiosas, ao mesmo tempo, evitando danos ou intrusões maliciosas ou acidentais. O subconjunto ADsafe pode ser verificado mecanicamente por ferramentas como JSLint de modo que nenhuma inspeção humana é necessária para rever o código de segurança dos convidados. O subconjunto ADsafe também impõe boas práticas de codificação, aumentando a probabilidade de que o código do hóspede será executado corretamente.

Poderá ver um exemplo de como usar o ADsafe, olhando para os ficheiros template.html e template.js no repositório GitHub do projecto .

 32
Author: Simon Lieschke, 2017-01-16 20:52:36

Criei uma biblioteca de caixa de areia chamada jsandbox que usa web workers para avaliar o código. Ele também tem um método de entrada para dar explicitamente dados de código sandbox que de outra forma não seria capaz de obter.

O seguinte é um exemplo da 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
      }
  });
 24
Author: Eli Grey, 2010-11-10 20:56:08

EDIT : Embora eu não saiba de uma maneira de escapar da lista de brancos abaixo, eu iria executar o trabalhador a partir de uma caixa de areia <iframe> também, apenas no caso, e iria segundo js do @gronostaj.recomendação js se a exaustão da memória é uma preocupação.

Os Web workers fornecem uma maneira conveniente de criar outro contexto de script, que pode então ser agressivamente sandboxed sem afetar o pai.

Uma implementação com promessas:

function safeEval(untrustedCode) {
    return new Promise(function (resolve, reject) {
        var worker = new Worker('eval.js');

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

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

        worker.postMessage(untrustedCode);

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

eval.js (provavelmente vais querer. expandir a lista de brancos):

(function (global) {
    'use strict';

    var _postMessage = postMessage;
    var _addEventListener = addEventListener;

    (function () {
        var current = global;
        var keepProperties = [
            // required
            'Object', 'Function', 'Infinity', 'NaN', 'undefined',
            // 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);
    })();

    _addEventListener('message', function (e) {
        var f = new Function('', 'return (' + e.data + '\n);');
        _postMessage(f());
    });
})(this);

Nenhum acesso UI dos trabalhadores, pode ser terminado separadamente, não pode amarrar o UI ou o script de execução, e são padrão.

 20
Author: Ry-, 2016-09-25 07:23:05

Acho que js.vale a pena mencionar aqui o js. É um interpretador JavaScript escrito em JavaScript.

É cerca de 200 vezes mais lento que o JS nativo, mas a sua natureza faz dele um ambiente perfeito. Outra desvantagem é seu tamanho-quase 600 kb, que pode ser aceitável para desktops em alguns casos, mas não para dispositivos móveis.
 7
Author: gronostaj, 2014-07-09 17:36:07

Como mencionado em outras responses, é suficiente prender o código em sandboxed iframe (sem enviá-lo para o lado do servidor) e se comunicar com as mensagens. Eu sugeriria dar uma olhada em uma biblioteca pequena que eu criei principalmente por causa da necessidade de fornecer alguma API para o código não confiável, assim como descrito na pergunta: há uma oportunidade de exportar o conjunto particular de funções para a caixa de areia onde o código não confiável é executado. E há também uma demo que executa o código enviado por um utilizador numa caixa de areia:

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

 6
Author: asvd, 2015-01-19 11:02:58

Todos os fornecedores do navegador e a especificação HTML5 estão a trabalhar para uma propriedade real da caixa de areia para permitir as iframes de caixa de areia -- mas ainda está limitada à granularidade da iframe.

Em geral, nenhum grau de Expressões Regulares, etc. pode sanitar de forma segura o usuário arbitrário fornecido JavaScript como ele degenera para o problema de parada: - /

 4
Author: olliej, 2014-08-19 17:33:47
[[[2]} de uma maneira feia, mas talvez isso funcione para você , eu peguei todos os globais e redefiniu-os no âmbito da caixa de areia, bem como eu adicionei o modo estrito para que eles não possam obter o objeto global usando uma função anônima.
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

 2
Author: alejandro, 2014-03-06 03:59:59

Uma versão melhorada do Código sandbox dos trabalhadores da web de @RyanOHara, num ficheiro único (não é necessário nenhum ficheiro extra 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);
    });
    }

Testa-o:

Https://jsfiddle.net/kp0cq6yw/

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

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

Deve produzir 6 (testado em cromado e Firefox).

 2
Author: MarcG, 2016-05-11 06:34:16

Um interpretador de Javascript independente é mais provável que produza uma caixa de areia robusta do que uma versão enjaulada da implementação do navegador builtin. O Ryan já mencionou ... js.js , mas um projecto mais actualizado é O intérprete js. O docs cobre como expor várias funções ao intérprete, mas o seu alcance é muito limitado.

 0
Author: David Fraser, 2017-05-23 12:10:11

De onde vem este utilizador JavaScript?

Não há muito que possa fazer acerca de um código incorporado pelo utilizador na sua página e depois chamá-lo a partir do seu navegador (ver Greasemonkey, http://www.greasespot.net é algo que os navegadores fazem.

No entanto, se você armazenar o script em uma base de dados, em seguida, recuperá-lo e eval() ele, então você pode limpar o script antes de ser executado.

Exemplos de código que remove todas as janelas. e documentar. referências:

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

Isto tenta impedir que sejam executados os seguintes ensaios (não testados):

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

Existem muitas limitações que você teria que aplicar ao script de usuário inseguro. Infelizmente, não há nenhum' recipiente de caixa de areia ' disponível para JavaScript.

 -4
Author: Dimitry, 2008-10-12 06:44:04

1) suponha que tem um código para executar:

var sCode = "alert(document)";
Agora, suponha que você quer executá-lo em uma caixa de areia:
new Function("window", "with(window){" + sCode + "}")({});

Estas duas linhas quando executadas falharão, porque a função" alerta " não está disponível na "caixa de areia"

2) e agora quer expor um membro do objecto window com a sua funcionalidade:

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

Realmente você pode adicionar citações escapando e fazer outro polimento, mas eu acho que a idéia é clara.
 -4
Author: Sergey Ilinsky, 2014-08-17 09:01:33

Você pode embrulhar o código do utilizador numa função que redefine os objectos proibidos como parâmetros -- estes seriam então undefined quando chamados:

(function (alert) {

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

}) ();

Claro, os atacantes inteligentes podem contornar isto inspeccionando o DOM Javascript e encontrando um objecto não sobreposto que contém uma referência à janela.


Outra ideia é digitalizar o código do utilizador usando uma ferramenta como jslint . Certifique-se de que está definido para não ter variáveis predefinidas (ou: apenas variáveis que deseja), e então, se quaisquer globals são definidos ou acessados, não deixe o script do Usuário ser usado. Novamente, pode ser vulnerável a caminhar no DOM -- objetos que o usuário pode construir usando literais podem ter referências implícitas ao objeto da janela que pode ser acessado para escapar da caixa de areia.

 -5
Author: John Millikin, 2008-10-12 06:26:01
Tenho estado a trabalhar numa caixa de areia simplista para deixar os utilizadores construírem applets para o meu site. Embora eu ainda enfrente alguns desafios com a permissão de acesso DOM (parentNode simplesmente não me deixa manter as coisas seguras =/), minha abordagem foi apenas redefinir o objeto janela com alguns de seus membros úteis/inofensivos, e então eval () o código do usuário com esta janela redefinida como o escopo padrão. O meu código "núcleo" é assim... (Eu não estou mostrando totalmente;)
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);
            }
        }
    };
}
Então, eu posso ... instance uma caixa de areia e use seu execute () para obter o código em execução. Além disso, todas as novas variáveis declaradas dentro do Código eval'd serão, em última análise, ligadas ao escopo executar (), de modo que não haverá clashing nomes ou mexer com o código existente.

Embora os objectos globais ainda sejam acessíveis, aqueles que devem permanecer desconhecidos do Código Sandbox devem ser definidos como proxies na caixa de areia::objecto de âmbito.

Espero que isto funcione para ti.
 -5
Author: , 2009-05-20 14:10:09