A função eval é uma maneira fácil e poderosa de gerar código dinamicamente. Quais são as advertências?
A função eval é uma maneira fácil e poderosa de gerar código dinamicamente. Quais são as advertências?
Respostas:
O uso inadequado de eval abre seu código para ataques de injeção
A depuração pode ser mais desafiadora (sem números de linha etc.)
código eval'd executa mais lentamente (sem oportunidade de compilar / armazenar em cache código eval'd)
Edit: Como @Jeff Walden aponta nos comentários, o # 3 é menos verdadeiro hoje do que em 2008. No entanto, embora ocorra algum cache de scripts compilados, isso será limitado apenas a scripts que são eval'd repetidos sem 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. Digamos apenas que ALGUM código avaliado é executado mais lentamente.
badHackerGuy'); doMaliciousThings();
e se você pegar meu nome de usuário, concatê-lo em algum script e avaliá-lo nos navegadores de outras pessoas, eu posso executar qualquer javascript que desejar nas máquinas deles (por exemplo, forçá-los a marcar com +1 minhas postagens, postar seus dados no meu servidor, etc.)
debugger;
instrução ao seu código-fonte. Isso interromperá a execução do seu programa nessa linha. Depois disso, você pode adicionar pontos de interrupção de depuração como se fosse apenas outro arquivo JS.
avaliação nem sempre é má. Há momentos em que é perfeitamente apropriado.
No entanto, o eval é atualmente e historicamente excessivamente utilizado por pessoas que não sabem o que estão fazendo. Infelizmente, isso inclui pessoas que escrevem tutoriais sobre JavaScript e, em alguns casos, isso pode ter consequências de segurança - ou, mais frequentemente, bugs simples. Portanto, quanto mais podemos fazer para colocar um ponto de interrogação sobre a avaliação, melhor. Sempre que você usa o eval, você precisa verificar a sanidade do que está fazendo, porque é possível que esteja fazendo isso de uma maneira melhor, mais segura e mais limpa.
Para dar um exemplo muito típico, defina 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 idéia sobre o básico de como os objetos JavaScript funcionam, eles teriam percebido que colchetes podem ser usados em vez de nomes de pontos literais, evitando a necessidade de avaliação:
document[potato].style.color = 'red';
... o que é muito mais fácil de ler, além de ter menos bugs.
(Mas então, alguém que realmente sabia o que estava fazendo diria:
document.getElementById(potato).style.color = 'red';
que é mais confiável do que o velho truque de acessar elementos DOM diretamente do objeto de documento.)
JSON.parse()
em vez de eval()
para JSON?
Eu acredito que é porque ele pode executar qualquer função JavaScript de uma string. Seu uso facilita a injeção de código não autorizado no aplicativo.
Dois pontos vêm à mente:
Segurança (mas, desde que você gere a sequência a ser avaliada, isso pode ser um problema)
Desempenho: até que o código a ser executado seja desconhecido, ele não pode ser otimizado. (sobre javascript e desempenho, certamente a apresentação de Steve Yegge )
eval
uated e, em seguida, aquele pequeno pedaço de código é executado no navegador do usuário B
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 pesado.
Principalmente, é muito mais difícil de manter e depurar. É como um goto
. Você pode usá-lo, mas torna mais difícil encontrar problemas e mais difícil para as pessoas que precisam fazer alterações mais tarde.
Uma coisa a ter em mente é que você pode usar eval () para executar código em um ambiente restrito - sites de redes sociais que bloqueiam funções JavaScript específicas às vezes podem ser enganados dividindo-os em um bloco eval -
eval('al' + 'er' + 't(\'' + 'hi there!' + '\')');
Portanto, se você estiver olhando para executar algum código JavaScript onde, de outra forma, não seria permitido ( Myspace , estou olhando para você ...), eval () pode ser um truque útil.
No entanto, por todos os motivos mencionados acima, você não deve usá-lo para o seu próprio código, onde você tem controle total - isso não é necessário e a melhor situação é relegada à prateleira 'hacks JavaScript complicados'.
[]["con"+"struc"+"tor"]["con"+"struc"+"tor"]('al' + 'er' + 't(\'' + 'hi there!' + '\')')()
A menos que você permita a eval () um conteúdo dinâmico (por meio de cgi ou entrada), ele é tão seguro e sólido quanto todos os outros JavaScript da sua página.
Juntamente com o restante das respostas, não acho que as declarações de avaliação possam ter minimização avançada.
É um possível risco à segurança, possui 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 mais informações: eval .
É bastante útil, porém, e usado com moderação pode adicionar muitas boas funcionalidades.
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.
Eu sei que essa discussão é antiga, mas eu realmente gosto disso abordagem do Google e queria compartilhar esse sentimento com outras pessoas;)
A outra coisa é que quanto melhor você fica, mais você tenta entender e, finalmente, você simplesmente não acredita que algo seja bom ou ruim, apenas porque alguém disse isso :) Este é um vídeo muito inspirador que me ajudou a pensar mais sozinho :) BOAS PRÁTICAS são boas, mas não as use sem pensar :)
Não é necessariamente tão ruim, desde que você saiba em que contexto o está usando.
Se seu aplicativo estiver usando eval()
para criar um objeto a partir de algum JSON que retornou de um XMLHttpRequest para o seu próprio site, criado pelo seu código confiável do lado do servidor, provavelmente não é um problema.
O código JavaScript não confiável do lado do cliente não pode fazer tanto assim. Desde que o que você esteja executando eval()
tenha vindo de uma fonte razoável, você estará bem.
Isso reduz bastante o seu nível de confiança sobre segurança.
Se você deseja que o usuário insira algumas funções lógicas e avalie para AND o OR, a função JavaScript eval é perfeita. Eu posso aceitar duas cordas e eval(uate) string1 === string2
etc.
Se você detectar o uso de eval () em seu código, lembre-se do mantra "eval () é mau".
Essa função pega uma sequência arbitrária e a executa como código JavaScript. Quando o código em questão é conhecido antecipadamente (não determinado em tempo de execução), não há razão para usar eval (). Se o código é gerado dinamicamente no tempo de execução, geralmente há uma maneira melhor de atingir a meta sem eval (). Por exemplo, apenas usar a notação de colchete 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 eval()
também tem implicações de segurança, porque você pode estar executando um código (por exemplo, proveniente da rede) que foi violado. 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 suportamJSON.parse()
nativo, você pode usar uma biblioteca do JSON.org.
Também é importante lembrar que as cordas que passam a setInterval()
, setTimeout()
e, o Function()
construtor é, em sua maior parte, semelhante ao uso eval()
e, portanto, deve ser evitado.
Nos bastidores, o JavaScript ainda precisa 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);
O uso do novo construtor Function () é semelhante ao eval () e deve ser abordado com cuidado. Pode ser uma construção poderosa, mas geralmente é mal utilizada. Se você absolutamente deve usareval()
, considere usar a nova Function ().
Há um pequeno benefício em 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 impedir globals automáticos é envolver a
eval()
chamada em uma função imediata.
Além dos possíveis problemas de segurança, se você estiver executando um código enviado pelo usuário, na maioria das vezes há uma maneira melhor que não envolve a análise do código toda vez que ele é executado. Funções anônimas ou propriedades de objetos podem substituir a maioria dos usos de avaliação e são muito mais seguras e rápidas.
Isso pode se tornar mais um problema, pois a próxima geração de navegadores sai com algum sabor de um compilador JavaScript. O código executado através do Eval pode não ter um desempenho tão bom quanto o restante do JavaScript nesses navegadores mais recentes. Alguém deve fazer alguns perfis.
Este é um dos bons artigos falando sobre eval e como ele 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 correndo e começar a usar eval () em todos os lugares. De fato, existem muito poucos casos de uso bons para executar eval (). Definitivamente, existem preocupações com a clareza do código, a depuração e certamente o 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 que ninguém o assuste pensando que seu código é mais frágil ou menos seguro quando eval () for usado adequadamente.
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 o uso de eval (), mas vamos apenas dizer como a string que você está executando com eval () é afetada por uma parte mal-intencionada. No final, você estará executando um código malicioso. Com poder vem uma grande responsabilidade. Portanto, use-o com sabedoria se você estiver 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ê está procurando os conceitos básicos de eval () veja aqui: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
O JavaScript Engine possui diversas otimizações de desempenho que são executadas durante a fase de compilação. Algumas delas se resumem a ser capaz de analisar estaticamente o código conforme ele é lexical e determinar previamente onde estão todas as declarações de variáveis e funções, para que seja necessário menos esforço para resolver identificadores durante a execução.
Mas se o Mecanismo encontrar um eval (..) no código, ele deve essencialmente assumir que toda a sua percepção da localização do identificador pode ser inválida, porque não pode saber no momento do lexing exatamente qual código você pode passar para o eval (..) para modificar o escopo lexical ou o conteúdo do objeto ao qual você pode passar para criar um novo escopo lexical a ser consultado.
Em outras palavras, no sentido pessimista, a maioria dessas otimizações seria inútil se eval (..) estivesse presente; portanto, simplesmente não realiza 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
Nem sempre é uma má ideia. Tomemos, por exemplo, geração de código. Recentemente, escrevi uma biblioteca chamada Hyperbars, que preenche a lacuna entre o virtual-dom e o guidão . Isso é feito analisando um modelo de guidão e convertendo-o em hiperescrito, que é posteriormente usado pelo virtual-dom. O hiperescrito é gerado como uma string primeiro e, antes de devolvê-lo, eval()
transforma-o em código executável. Eu encontrei eval()
nessa situação em particular o exato 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 sequência gerada uma vez e, em seguida, reutilizar a saída executável várias vezes.
Você pode ver como a geração de código foi alcançada se você estiver curioso aqui .
Eu diria que isso realmente não importa se você usa eval()
javascript que é executado em navegadores. * (Ressalva)
Todos os navegadores modernos têm um console de desenvolvedor onde você pode executar javascript arbitrário de qualquer maneira e qualquer desenvolvedor semi-inteligente pode examinar sua fonte JS e colocar os bits necessários no console de desenvolvimento para fazer o que quiser.
* Desde que os pontos de extremidade do servidor tenham a validação e higienização corretas dos valores fornecidos pelo usuário, não importa o que é analisado e avaliado no javascript do lado do cliente.
eval()
No entanto, se você perguntar se é adequado usar no PHP, a resposta é NÃO , a menos que você coloque na lista branca quaisquer valores que possam ser passados para sua declaração de avaliação.
Não tentarei refutar nada do que foi dito até agora, mas vou oferecer esse uso de eval () que (tanto quanto eu sei) não pode ser feito de outra maneira. Provavelmente existem outras maneiras de codificar isso, e provavelmente maneiras de otimizá-lo, mas isso é feito à mão e sem sinos e assobios, para fins de clareza, para ilustrar o uso de eval que realmente não tem outras alternativas. Ou seja: nomes de objetos dinâmicos (ou mais precisamente) criados por programa (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: a propósito, 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. Não consigo imaginar nenhuma boa razão para você querer fazer isso. Ainda assim, pensei em ressaltar que não seria uma boa ideia :)
namespaceToTest[namespaceParts[i]]
, não há necessidade de eval aqui, então a if(typeof namespaceToTest[namespaceParts[i]] === 'undefined') { namespaceToTest[namespaceParts[i]] = {};
única diferença para oelse namespaceToTest = namespaceToTest[namespaceParts[i]];
Coleta de lixo
A coleta de lixo dos navegadores não tem idéia se o código avaliado pode ser removido da memória, mantendo-o armazenado até que a página seja recarregada. Não é tão ruim se seus usuários estão apenas na sua página em breve, mas pode ser um problema para os aplicativos da web.
Aqui está um script para demonstrar o problema
https://jsfiddle.net/CynderRnAsh/qux1osnw/
document.getElementById("evalLeak").onclick = (e) => {
for(let x = 0; x < 100; x++) {
eval(x.toString());
}
};
Algo tão simples quanto o código acima faz com que uma pequena quantidade de memória seja armazenada até que o aplicativo morra. Isso é pior quando o script avaliado é uma função gigante e é chamado em intervalo.