Vm2: Não imune ao tempo (1) {}

Criado em 2 jan. 2019  ·  19Comentários  ·  Fonte: patriksimek/vm2

O código a seguir não atingirá o tempo limite.

const {VM} = require('vm2');
new VM({timeout:1}).run(`
        function main(){
        while(1){}
    }
    new Proxy({}, {
        getPrototypeOf(t){
            global.main();
        }
    })`);
bug confirmed help wanted

Todos 19 comentários

Provavelmente não há uma boa solução para isso. NodeJS é um processo único. A proteção while (1) {} requer multiprocessos para que o verificador não seja bloqueado pelo while (1) {}. Talvez se pudéssemos reacender o projeto SpiderNode .......

Isso é causado por Decontextify.value testando o protótipo do objeto convidado com instanceOf . O módulo Node.js vm é imune a isso. O patch de um homem pobre pode ser backdoor Proxy para que pudéssemos detectar qualquer objeto Proxy da sandbox, mas uma abordagem ideal seria simplesmente nunca confiar em nenhum objeto e função dele e colocar contextify.js na sandbox, usando valores primitivos para se comunicar e manipular para fazer referência a objetos convidados.

Só para as pessoas verem, o browserless usa um processo filho e passagem de mensagens para falar com o filho que está executando o código não confiável. É um pouco uma tarefa de configuração, mas uma vez feito isso, você estará imune a while(1){} ataques. A fonte está aqui .

O objetivo deste projeto parece ser não gerar um todo separado
Processo do sistema operacional. Caso contrário, existem muitas soluções disponíveis que são muito
mais seguro.

Caso contrário, existem muitas soluções disponíveis que são muito mais seguras.

Quais? Não tenho certeza se há uma alternativa melhor para VM2 no NodeJS, pelo menos não que eu tenha visto

O mantenedor desta biblioteca apontou para esta alternativa na edição # 80:

https://github.com/laverdet/isolated-vm

Eu não tentei ainda.

Eu apenas executo código não confiável em processo filho. Não pode arriscar executá-lo na instância principal do V8, especialmente quando você sabe que é de thread único.

O problema de chamar o motor V8 diretamente é a falta de módulo de importação. No meu caso, preciso importar o módulo de solicitação para permitir operações HTTP. Ele também deve ser assíncrono.

Acho vm2 + child_process uma boa combinação.

Ei,

Eu e @harelmo criamos uma biblioteca em cima do vm2 que é imune a while(1) {} .

Semelhante ao que tian-ma mencionou, mas usamos threads de trabalho node.js em vez de processos filhos.

dê uma olhada aqui: https://www.npmjs.com/package/isolated-runtime e diga-nos o que você achou :)

Outra solução alternativa seria simplesmente desativar Proxy na sandbox, certo?

const vm = new VM({
    sandbox: {
        Proxy: undefined
    },
    timeout: 100
});

Usando o Promise, também é possível escrever um código que nunca atinge o tempo limite:

"use strict";
const {VM} = require('vm2');
const untrusted = '(' + function(){
    Promise.resolve().then(a=>{
        while(1){}
    });
}+')()';
try{
    console.log(new VM({timeout:10}).run(untrusted));
}catch(x){
    console.log(x);
}

O problema aqui é que. Então enfileirará a função para ser chamada mais tarde na fila de microtarefa.
Para corrigir isso, precisamos escrever nossa própria Promise.then e funções relacionadas ou desativar Promise.

Também funciona:

const {VM} = require('vm2');

const script = '(' + function() {
    (async function x(){
        await {then: y=>y()};
        while(1){}
    })();
}+')()';

new VM({timeout:10}).run(script);

Portanto, apenas substituir a Promessa não funcionará.
Não tenho ideia de como consertar isso.

Enquanto isso permanece aberto, devemos adicionar um esclarecimento ao README para alertar os usuários em potencial. Essa vulnerabilidade permite escapar da sandbox e, portanto, vai contra o propósito da biblioteca.

Leiame foi atualizado https://github.com/patriksimek/vm2/commit/77f5265ab53b87864a312156ee62b1082787e9b0#diff -04c6e90faac2675aa89e2176d2eec7d8. E eu não sei como isso poderia ser usado para escapar da sandbox, você só pode escapar do período de tempo pretendido em que o script deve ser executado.

Talvez este seja o seu próprio problema, mas por que o NodeVM não oferece suporte a "tempo limite"?

@crowder já que em NodeVM existe setTimeout, setInterval e setImmediate, seria fácil contornar o tempo limite. Talvez haja outros motivos também, mas não trabalho com NodeVM, apenas VM.

@XmiliaH não impediria a implementação de um timeout pelo menos de loops infinitos não intencionais?
A combinação mal-intencionada while (true) + setTimeout ainda seria um problema, mas qualquer loop infinito não intencional simples feito por engano poderia ser tratado com o suporte a timeout

Minha preocupação é que o NodeVM permite exigir módulos que podem ser módulos de host e o tempo limite enquanto eles mudam de estado global pode ser ruim. Além disso, o NodeVM retorna um módulo e os usuários muito provavelmente chamariam funções expostas pelo módulo, no entanto, o tempo limite só se aplicaria ao carregamento do módulo e não às chamadas de função.
Se você quiser chamar uma função com um tempo limite, você pode simplesmente implementá-la como

function doWithTimeout(fn, timeout) {
    let ctx = CACHE.timeoutContext;
    let script = CACHE.timeoutScript;
    if (!ctx) {
        CACHE.timeoutContext = ctx = vm.createContext();
        CACHE.timeoutScript = script = new vm.Script('fn()', {
            filename: 'timeout_bridge.js',
            displayErrors: false
        });
    }
    ctx.fn = fn;
    try {
        return script.runInContext(ctx, {
            displayErrors: false,
            timeout
        });
    } finally {
        ctx.fn = null;
    }
}

Bom argumento @XmiliaH e obrigado pelo exemplo
Vejo que seu exemplo está usando o pacote vm e não vm2
Seria bom ser capaz de usar vm2 e ainda ser capaz de tempo limite

@szydan O código que postei é usado pela VM2 para expirar o tempo da VM. Se o NodeVM também tivesse um tempo limite, ele usaria a mesma funcionalidade.

Esta página foi útil?
0 / 5 - 0 avaliações