ti-enxame.com

Por que usar a função eval do JavaScript é uma má ideia?

A função eval é uma maneira poderosa e fácil de gerar código dinamicamente, então, quais são as ressalvas?

503
Brian Singh
  1. Uso indevido de eval abre seu código para ataques de injeção

  2. Depuração pode ser mais desafiador (sem números de linha, etc.)

  3. o código eval'd é executado mais lentamente (sem oportunidade de compilar/armazenar em cache o código eval'd)

Edit: Como @Jeff Walden aponta em comentários, # 3 é menos verdade hoje do que era em 2008. No entanto, enquanto alguns caches de scripts compilados podem acontecer, isso só será limitado a scripts que são repetidos com qualquer modificação. Um cenário mais provável é que você esteja avaliando scripts que sofreram pequenas modificações a cada vez e, como tal, não puderam ser armazenados em cache. Vamos apenas dizer que ALGUM código eval'd é executado mais lentamente.

369
Prestaul

eval nem sempre é mal. Há momentos em que é perfeitamente apropriado.

No entanto, a eval é atualmente e historicamente superutilizada por pessoas que não sabem o que estão fazendo. Isso inclui pessoas que escrevem tutoriais de JavaScript, infelizmente, e em alguns casos isso pode ter conseqüências de segurança - ou, mais frequentemente, erros simples. Então, quanto mais pudermos fazer para lançar um ponto de interrogação sobre eval, melhor. Toda vez que você usa o eval, precisa verificar o que está fazendo, porque é possível que você esteja fazendo isso de uma maneira melhor, mais segura e mais limpa.

Para dar um exemplo muito típico, para definir a cor de um elemento com um id armazenado na variável 'potato':

eval('document.' + potato + '.style.color = "red"');

Se os autores do tipo de código acima tivessem uma pista sobre o básico de como os objetos JavaScript funcionam, eles perceberiam que colchetes podem ser usados ​​em vez de nomes de pontos literais, evitando a necessidade de eval:

document[potato].style.color = 'red';

... o que é muito mais fácil de ler, bem como menos potencialmente bugs.

(Mas alguém que realmente sabia o que estava fazendo diria:

document.getElementById(potato).style.color = 'red';

que é mais confiável do que o antigo truque de acessar elementos DOM diretamente do objeto de documento.)

343
bobince

Eu acredito que é porque ele pode executar qualquer função JavaScript de uma string. O uso torna mais fácil para as pessoas injetarem código não autorizado no aplicativo.

36
kemiller2002

Dois pontos vêm à mente:

  1. Segurança (mas, desde que você gere a string para ser avaliada, isso pode ser um problema)

  2. Performance: até que o código a ser executado seja desconhecido, não pode ser otimizado. (sobre javascript e performance, certamente apresentação de Steve Yegge )

26
xtofl

Passar a entrada do usuário para eval () é um risco de segurança, mas também cada chamada de eval () cria uma nova instância do interpretador JavaScript. Isso pode ser um recurso de porco.

20
Andrew Hedges

Geralmente, é apenas um problema se você está passando a entrada do usuário eval.

17
Mark Biek

Principalmente, é muito mais difícil de manter e depurar. É como um goto. Você pode usá-lo, mas fica mais difícil encontrar problemas e dificuldades nas pessoas que podem precisar fazer alterações mais tarde.

15
Brian

Uma coisa a ter em mente é que muitas vezes você pode usar o eval () para executar código em um ambiente restrito - os sites de redes sociais que bloqueiam funções específicas de JavaScript podem ser enganados ao dividi-los em um bloco de avaliação -

eval('al' + 'er' + 't(\'' + 'hi there!' + '\')');

Então, se você está procurando executar algum código JavaScript onde ele não poderia ser permitido ( Myspace , estou olhando para você ...) então eval () pode ser um truque útil.

No entanto, por todas as razões mencionadas acima, você não deve usá-lo para o seu próprio código, onde você tem controle completo - não é necessário, e é melhor que seja relegado à estante de 'hacks complicados de JavaScript'.

13
matt lohkamp

A menos que você deixe eval () um conteúdo dinâmico (através de cgi ou input), ele é tão seguro e sólido quanto todos os outros JavaScript em sua página.

11
Thevs

Juntamente com o resto das respostas, não acho que as declarações eval possam ter minimização avançada.

7
Paul Mendoza

É um risco de segurança possível, tem um escopo de execução diferente e é bastante ineficiente, pois cria um ambiente de script totalmente novo para a execução do código. Veja aqui para mais algumas informações: eval .

É bastante útil, porém, e usado com moderação pode adicionar muita boa funcionalidade.

6
Tom

Eu sei que esta discussão é antiga, mas eu realmente gosto desta abordagem do Google e queria compartilhar esse sentimento com os outros;)

A outra coisa é que quanto melhor você começa, mais você tenta entender e, finalmente, você simplesmente não acredita que algo é bom ou ruim só porque alguém disse isso :) Isso é muito inspirador vídeo que me ajudou a pensar mais por mim mesmo :) BOAS PRÁTICAS são boas, mas não as use sem pensar :)

5
op1ekun

A menos que você tenha 100% de certeza de que o código que está sendo avaliado é de uma fonte confiável (geralmente seu próprio aplicativo), é uma maneira infalível de expor seu sistema a um ataque de script entre sites.

5
John Topley

Não é necessariamente tão ruim, desde que você saiba em que contexto você está usando.

Se seu aplicativo estiver usando eval() para criar um objeto de algum JSON que tenha retornado de um XMLHttpRequest para seu próprio site, criado pelo seu código do lado do servidor confiável, provavelmente não é um problema.

Código JavaScript não confiável do lado do cliente não pode fazer muito de qualquer maneira. Desde que a coisa que você está executando eval() on tenha vindo de uma fonte razoável, você está bem.

5
MarkR

Isso reduz muito seu nível de confiança sobre segurança.

4
David Plumpton

Se você quiser que o usuário insira algumas funções lógicas e avalie para AND o OR então a função JavaScript eval é perfeita. Eu posso aceitar duas strings e eval(uate) string1 === string2, etc.

4
Ian

Se você detectar o uso de eval () em seu código, lembre-se do mantra “eval () é mal”.

Essa função usa uma string arbitrária e a executa como código JavaScript. Quando o código em questão é conhecido de antemão (não determinado em tempo de execução), não há razão para usar eval (). Se o código for gerado dinamicamente no tempo de execução, geralmente há uma maneira melhor de atingir a meta sem eval (). Por exemplo, apenas usando a notação de colchetes para acessar propriedades dinâmicas é melhor e mais simples:

// antipattern
var property = "name";
alert(eval("obj." + property));

// preferred
var property = "name";
alert(obj[property]);

O uso de eval() também tem implicações de segurança, porque você pode estar executando código (por exemplo, vindo da rede) que foi adulterado. Este é um antipadrão comum ao lidar com uma resposta JSON de uma solicitação Ajax. Nesses casos, é melhor usar os métodos internos dos navegadores para analisar a resposta JSON para garantir que ela seja segura e válida. Para navegadores que não suportam JSON.parse() nativamente, você pode usar uma biblioteca do JSON.org.

Também é importante lembrar que passar strings para setInterval(), setTimeout() e Function() é, na maioria das vezes, semelhante a eval() e, portanto, deve ser evitado.

Nos bastidores, o JavaScript ainda tem que avaliar e executar a string que você passa como código de programação:

// antipatterns
setTimeout("myFunc()", 1000);
setTimeout("myFunc(1, 2, 3)", 1000);

// preferred
setTimeout(myFunc, 1000);
setTimeout(function () {
myFunc(1, 2, 3);
}, 1000);

Usar o novo construtor Function () é semelhante a eval () e deve ser abordado com cuidado. Pode ser uma construção poderosa, mas é frequentemente mal utilizada. Se você absolutamente deve usar eval(), você pode considerar o uso de new Function ().

Há um pequeno benefício potencial porque o código avaliado em new Function () será executado em um escopo de função local, portanto, quaisquer variáveis ​​definidas com var no código que está sendo avaliado não se tornarão globais automaticamente.

Outra maneira de evitar globais automáticos é envolver a chamada eval() em uma função imediata.

3
12345678

Além dos possíveis problemas de segurança, se você estiver executando código enviado pelo usuário, na maioria das vezes, há uma maneira melhor de não envolver a nova análise do código toda vez que for executado. Funções anônimas ou propriedades de objeto podem substituir a maioria dos usos de eval e são muito mais seguras e rápidas.

2
Matthew Crumley

Isso pode se tornar mais um problema, já que a próxima geração de navegadores sai com algum sabor de um compilador JavaScript. Código executado via Eval pode não funcionar tão bem quanto o resto do seu JavaScript contra esses navegadores mais recentes. Alguém deveria fazer algum perfil.

2
Brian

eval () é muito poderoso e pode ser usado para executar uma instrução JS ou avaliar uma expressão. Mas a questão não é sobre os usos de eval (), mas vamos apenas dizer como a string que você está executando com eval () é afetada por uma parte maliciosa. No final você estará executando código malicioso. Com o poder vem uma grande responsabilidade. Então, use sabiamente, você está usando. Isso não está muito relacionado à função eval (), mas este artigo tem informações muito boas: http://blogs.popart.com/2009/07/javascript-injection-attacks/ Se você estiver procurando para o básico de eval () veja aqui: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval

2
Phi

Este é um dos bons artigos falando sobre eval e como não é um mal: http://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood/

Não estou dizendo que você deve sair e começar a usar o eval () em todos os lugares. Na verdade, existem poucos casos de uso bons para executar o eval (). Definitivamente há preocupações com clareza de código, depuração e, certamente, desempenho que não deve ser negligenciado. Mas você não deve ter medo de usá-lo quando tiver um caso em que eval () faça sentido. Tente não usá-lo primeiro, mas não deixe ninguém te assustar e pensar que seu código é mais frágil ou menos seguro quando eval () é usado adequadamente.

2
Amr Elgarhy

Não é sempre uma má ideia. Tomemos por exemplo, geração de código. Recentemente escrevi uma biblioteca chamada Hyperbars que preenche a lacuna entre virtual-dom e handlebars . Ele faz isso analisando um modelo de barras de guia e convertendo-o em hiperscrito , que é usado subseqüentemente pelo virtual-dom. O hiperescrito é gerado como uma string primeiro e antes de retorná-lo, eval() para transformá-lo em código executável. Eu encontrei eval() nesta situação particular exatamente o oposto do mal.

Basicamente de

<div>
    {{#each names}}
        <span>{{this}}</span>
    {{/each}}
</div>

Para isso

(function (state) {
    var Runtime = Hyperbars.Runtime;
    var context = state;
    return h('div', {}, [Runtime.each(context['names'], context, function (context, parent, options) {
        return [h('span', {}, [options['@index'], context])]
    })])
}.bind({}))

O desempenho de eval() não é um problema em uma situação como essa porque você só precisa interpretar a string gerada uma vez e depois reutilizar a saída executável várias vezes.

Você pode ver como a geração de código foi alcançada se você está curioso aqui .

1
Wikened

Eu não vou tentar refutar nada dito até agora, mas vou oferecer este uso de eval () que (tanto quanto eu sei) não pode ser feito de outra maneira. Há provavelmente outras maneiras de codificar isso e, provavelmente, maneiras de otimizá-lo, mas isso é feito à mão e sem sinos e assobios para ilustrar um uso de eval que realmente não tem outras alternativas. Ou seja: dinamicamente (ou mais precisamente) nomes de objetos criados por programação (em oposição a valores).

//Place this in a common/global JS lib:
var NS = function(namespace){
    var namespaceParts = String(namespace).split(".");
    var namespaceToTest = "";
    for(var i = 0; i < namespaceParts.length; i++){
        if(i === 0){
            namespaceToTest = namespaceParts[i];
        }
        else{
            namespaceToTest = namespaceToTest + "." + namespaceParts[i];
        }

        if(eval('typeof ' + namespaceToTest) === "undefined"){
            eval(namespaceToTest + ' = {}');
        }
    }
    return eval(namespace);
}


//Then, use this in your class definition libs:
NS('Root.Namespace').Class = function(settings){
  //Class constructor code here
}
//some generic method:
Root.Namespace.Class.prototype.Method = function(args){
    //Code goes here
    //this.MyOtherMethod("foo"));  // => "foo"
    return true;
}


//Then, in your applications, use this to instantiate an instance of your class:
var anInstanceOfClass = new Root.Namespace.Class(settings);

EDIT: por sinal, eu não sugeriria (por todas as razões de segurança apontadas até agora) que você baseie seus nomes de objetos na entrada do usuário. Eu não posso imaginar uma boa razão para você querer fazer isso. Ainda assim, pensei em apontar que não seria uma boa idéia :)

1
Carnix

Eu diria que não importa se você usa eval() em javascript que é executado em navegadores. * (Ressalva)

Todos os navegadores modernos têm um console de desenvolvedor onde você pode executar qualquer javascript arbitrário e qualquer desenvolvedor semi-inteligente pode olhar para o seu código fonte JS e colocar qualquer parte dele no console dev para fazer o que quiser.

* Desde que os endpoints do servidor tenham a validação correta dos valores fornecidos pelo usuário, não importa o que é analisado e avaliado no javascript do lado do cliente.

Se você perguntasse se é apropriado usar eval() in PHP, a resposta é NO, a menos que você whitelist quaisquer valores que possam ser passou para a sua declaração eval.

1
Adam Copley

O JavaScript Engine possui várias otimizações de desempenho que são executadas durante a fase de compilação. Algumas delas resumem-se à capacidade de analisar estaticamente, essencialmente, o código como um léxico, e pré-determinar onde estão todas as declarações de variáveis ​​e funções, de modo que são necessários menos esforços para resolver identificadores durante a execução.

Mas se o Engine encontrar um eval (..) no código, ele essencialmente tem que assumir que toda a sua consciência da localização do identificador pode ser inválida, porque ele não pode saber exatamente o código que você pode passar para eval (..) para modificar o escopo léxico, ou o conteúdo do objeto com o qual você pode passar para criar um novo escopo léxico a ser consultado.

Em outras palavras, no sentido pessimista, a maioria dessas otimizações seria inútil se eval (..) estivesse presente, por isso simplesmente não executa as otimizações.

Isso explica tudo.

Referência:

https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20&%20closures/ch2.md#eval

https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20&%20closures/ch2.md#performance

1
hkasera