Quando o eval () do JavaScript não é mau?

263

Estou escrevendo algum código JavaScript para analisar funções inseridas pelo usuário (para funcionalidade semelhante a planilha). Tendo analisado a fórmula, eu poderia convertê-lo em JavaScript e executá eval()-lo para produzir o resultado.

No entanto, sempre evitei usá-lo, eval()se posso evitá-lo porque é ruim (e, com ou sem razão, sempre achei que é ainda mais ruim no JavaScript, porque o código a ser avaliado pode ser alterado pelo usuário )

Então, quando é bom usá-lo?

Richard Turner
fonte
5
De fato, a maioria das bibliotecas JSON não utiliza avaliações de verdade, exatamente para proteger contra os riscos de segurança.
21713 Sean McMillan
11
@Sean - Ambos JQuery e Prototype uso eval (JQuery usa-lo via nova Function)
plodder
5
@ plodder - Onde você está obtendo suas informações? O jQuery utiliza o JSON.parse () nativo desde a versão 1.4 (em 1/2010)! Veja você mesmo: code.jquery.com/jquery-1.4.js
ken
3
"Obviamente, é necessário usar eval () para analisar JSON" - isso não é verdade, pelo contrário - não se deve usar eval para analisar JSON! Use o script json2.js de Douglas Crockfords (criador do JSON) em json.org !
TMS
11
@Tomas a ironia não sendo que json2.js usos eval para analisar JSON
tobyodavies

Respostas:

262

Gostaria de dedicar um momento para abordar a premissa de sua pergunta - que eval () é " mau ". A palavra " mal ", usada pelas pessoas da linguagem de programação, geralmente significa "perigoso", ou mais precisamente "capaz de causar muitos danos com um comando simples". Então, quando é bom usar algo perigoso? Quando você souber qual é o perigo e quando estiver tomando as precauções apropriadas.

Ao ponto, vamos olhar para os perigos no uso de eval (). Provavelmente existem muitos pequenos perigos ocultos, como todo o resto, mas os dois grandes riscos - a razão pela qual eval () é considerado mau - são desempenho e injeção de código.

  • Performance - eval () executa o interpretador / compilador. Se seu código for compilado, será um grande sucesso, pois você precisará chamar um compilador possivelmente pesado no meio do tempo de execução. No entanto, o JavaScript ainda é principalmente uma linguagem interpretada, o que significa que chamar eval () não é um grande problema de desempenho no caso geral (mas veja minhas observações específicas abaixo).
  • Injeção de código - eval () potencialmente executa uma cadeia de código sob privilégios elevados. Por exemplo, um programa em execução como administrador / raiz nunca desejaria avaliar a entrada do usuário, porque essa entrada poderia ser "rm -rf / etc / important-file" ou pior. Novamente, o JavaScript em um navegador não tem esse problema, porque o programa está sendo executado na própria conta do usuário. O JavaScript do servidor pode ter esse problema.

Para o seu caso específico. Pelo que entendi, você mesmo está gerando as seqüências de caracteres, assumindo o cuidado de não permitir que uma sequência como "rm -rf algo importante" seja gerada, não há risco de injeção de código (mas lembre-se, é muito difícil garantir isso no caso geral). Além disso, se você estiver executando no navegador, a injeção de código é um risco bem menor, acredito.

Quanto ao desempenho, você terá que ponderar isso contra a facilidade de codificação. É minha opinião que se você estiver analisando a fórmula, é melhor calcular o resultado durante a análise, em vez de executar outro analisador (o que está dentro de eval ()). Mas pode ser mais fácil codificar usando eval (), e o resultado do desempenho provavelmente será imperceptível. Parece que eval () neste caso não é mais mau do que qualquer outra função que possa poupar algum tempo.

user27476
fonte
78
Você não está abordando a questão de código que usa eval ser difícil de debug
bobobobo
48
A injeção de código é um problema muito sério para javascript, se você estiver preocupado com os dados do usuário. O código injetado será executado (no navegador) como se viesse do seu site, permitindo que ele fizesse qualquer tipo de travessia que o usuário poderia fazer manualmente. Se você permitir que o código (de terceiros) entre na sua página, ele poderá solicitar itens em nome do seu cliente ou alterar o gravatar ou o que eles puderem fazer no seu site. Tenha muito cuidado. Permitir que os hackers possuam seus clientes é tão ruim quanto permitir que eles possuam seu servidor.
21713 Sean McMillan
71
Se os dados vêm do seu servidor e são algo que você, o desenvolvedor gerou, não há mal nenhum em usar eval (). O verdadeiro dano é acreditar em tudo que você lê. Você vê muitas pessoas dizendo que eval () é ruim e elas não têm idéia do porquê, exceto que a leem em algum lugar.
Vince Panuccio 30/03
42
@Sean McMillan: Eu quero acreditar em você, mas se alguém interceptar e alterar o javascript eval()do seu servidor, ele também poderá alterar a fonte da página e também controlar as informações do usuário. . . Eu não vejo a diferença.
Walt W
20
Re "Injeção de código - ... Novamente, o JavaScript em um navegador não tem esse problema" e "Além disso, se você estiver executando no navegador, a injeção de código é um risco bem menor, eu acredito." Você está sugerindo que a injeção de código no navegador não é um problema? O XSS está entre as 3 principais vulns da lista dos 10 melhores da OWASP há vários anos.
Mike Samuel
72

eval()não é mau. Ou, se for, é ruim da mesma maneira que reflexão, E / S de arquivo / rede, encadeamento e IPC são "ruins" em outros idiomas.

Se, para o seu propósito , eval()é mais rápido que a interpretação manual, ou torna seu código mais simples ou mais claro ... então você deve usá-lo. Se não, então você não deveria. Simples assim.

Shog9
fonte
5
Um desses objetivos pode ser gerar código otimizado que seria muito longo ou muito repetitivo para ser escrito à mão. O tipo de coisa que, no LISP, exigiria uma macro.
wberry
5
Esse é um conselho tão geral que pode ser aplicado a literalmente qualquer bloco de código existente. Realmente não adiciona nada a esta pergunta; em particular, não ajuda ninguém a vir aqui determinar se seu uso específico é problemático ou não.
Jpmc26
2
Mais rápido, mais simples, mais claro ... Esta resposta não cobre suficientemente as implicações de segurança.
Ruud Helderman
55

Quando você confia na fonte.

No caso do JSON, é mais ou menos difícil adulterar a fonte, porque ela vem de um servidor da web que você controla. Desde que o próprio JSON não contenha dados que um usuário fez upload, não há grande desvantagem em usar o eval.

Em todos os outros casos, eu me esforçava muito para garantir que os dados fornecidos pelo usuário estivessem em conformidade com minhas regras antes de alimentá-los para avaliar ().

Tomalak
fonte
13
Uma string json sempre deve ser testada com base na gramática json antes de usá-la em eval (). Portanto, a string json "{foo: alert ('XSS')}" não passaria, já que "alert ('XSS')" não é um valor adequado.
Gumbo
3
Bem, use HTTPS, então. OTOH: man-in-the-middle não é o cenário de ataque típico para o aplicativo da web de variedades de jardins, enquanto, por exemplo, é o script entre sites.
Tomalak
7
evaltambém não analisará corretamente todas as cadeias JSON válidas. Por exemplo, JSON.parse(' "\u2028" ') === "\u2028"mas eval(' "\u2028" ')gera uma exceção porque U + 2028 é uma nova linha em JavaScript, mas não é uma nova linha no que diz respeito ao JSON.
Mike Samuel
1
@ Justin - se o protocolo estiver comprometido, bem, normalmente o carregamento inicial da página teria sido enviado pelo mesmo protocolo, e é um ponto discutível, porque o cliente já está tão comprometido quanto possível.
Antinome
1
Lindamente disse @Tomalak, eu mencionei isso na minha resposta agora! Impressionante!
NiCk Newman
25

Vamos ter gente de verdade:

  1. Agora, todos os principais navegadores têm um console interno que seu possível hacker pode usar com abundância para invocar qualquer função com qualquer valor - por que eles se incomodariam em usar uma declaração de avaliação - mesmo que pudessem?

  2. Se demorar 0,2 segundos para compilar 2000 linhas de JavaScript, qual é a minha degradação de desempenho se avaliar quatro linhas de JSON?

Até a explicação de Crockford para 'eval é mau' é fraca.

eval é Evil, a função eval é o recurso mais mal utilizado do JavaScript. Evite isso

Como o próprio Crockford pode dizer: "Esse tipo de afirmação tende a gerar neurose irracional. Não compre".

Compreender a avaliação e saber quando isso pode ser útil é muito mais importante. Por exemplo, eval é uma ferramenta sensata para avaliar as respostas do servidor que foram geradas pelo seu software.

BTW: Prototype.js chama eval diretamente cinco vezes (incluindo em evalJSON () e evalResponse ()). O jQuery usa-o em parseJSON (via construtor Function).

plodder
fonte
10
O JQuery usa a função JSON.parse interna do navegador, se disponível (o que é muito mais rápido e seguro), usando eval apenas como um mecanismo de fallback. A afirmação "eval is evil" é uma orientação razoavelmente boa.
Jjmontes
30
Re "Todo navegador principal agora tem um console embutido ...". A injeção de código é um problema quando um usuário pode inserir um código que é executado no navegador de outro usuário. Os consoles do navegador por si só não permitem que um usuário execute o código em outro navegador, portanto, são irrelevantes ao decidir se vale a pena proteger contra a injeção de código.
Mike Samuel
28
"Todos os principais navegadores agora têm um console embutido ... por que eles se preocupariam em usar uma declaração de avaliação?" - Você está muito errado. Eu sugiro que você edite a resposta. A capacidade de um usuário injetar código que pode ser executado no navegador de outro é um grande problema. E é aqui que você precisa ser realmente real.
akkishore
5
@akkishore, eu aprecio se você apresentar um exemplo da vida real que suporte suas declarações acima declaradas.
Akash Kava
7
@AkashKava O que você não está conseguindo perceber é que, se eu enviar javascript na minha caixa de comentários, e esse javascript o fizer no banco de dados. Quando outro usuário visualiza esse comentário (no qual eu coloquei o javascript), o eval pega esse javascript quando ele é renderizado e o avalia usando o intérprete, fazendo com que o meu javascript incorporado seja executado no navegador do outro usuário. Ao fazer isso, posso reunir todo tipo de informação. O nome de usuário, a identificação de usuário no banco de dados, o endereço de e-mail etc. Essa não é uma resposta difícil. Se você tivesse pesquisado no XSS no Google, veria em 10 segundos o motivo.
Kry Richter #
18

I tendem a seguir o conselho do Crockford para eval(), e evitá-lo completamente. Mesmo as maneiras que parecem exigir isso não o fazem. Por exemplo, o setTimeout()permite passar uma função em vez de avaliar.

setTimeout(function() {
  alert('hi');
}, 1000);

Mesmo que seja uma fonte confiável , eu não a uso, porque o código retornado pelo JSON pode ser ilegível, o que pode, na melhor das hipóteses, fazer algo complicado, na pior das hipóteses, expor algo ruim.

Swilliams
fonte
2
Eu acho que os erros no formatador JSON no lado do servidor são certamente um problema. A resposta do servidor depende de qualquer tipo de texto enviado pelo usuário? Então você precisa prestar atenção no XSS.
22480
3
Se seu servidor da web não for autenticado via HTTPS, você poderá sofrer algum tipo de ataque man-in-the-middle, em que outro host intercepta a solicitação e envia seus próprios dados.
Ben Combee
11
Se alguém puder executar um ataque man-in-the-middle, poderá injetar qualquer coisa facilmente nos seus scripts.
El.pescado 5/05
10
Você não deve confiar absolutamente no seu código javascript ... Você não depende de nada que seja executado no lado do cliente ... Se alguém faz um ataque man-in-the-middle, por que ele mexeria com seus objetos json? Ele pode servir uma página diferente para você e diferentes js arquivos ...
Calmarius
5
Pessoalmente, não gosto do argumento "sempre há outras maneiras de fazê-lo". Por exemplo, você também pode dizer que sempre há maneiras de evitar a programação orientada a objetos. Isso não significa que não é uma ótima opção. Se você entende eval e seus perigos, pode ser uma ótima ferramenta para usar nas situações certas.
dallin
4

Vi pessoas advogadas a não usar eval, porque é ruim , mas vi as mesmas pessoas usarem Function e setTimeout dinamicamente, então elas usam eval sob os capuzes : D

BTW, se a sua sandbox não tiver certeza suficiente (por exemplo, se você estiver trabalhando em um site que permita a injeção de código), eval é o último dos seus problemas. A regra básica de segurança é que todas as entradas são ruins, mas, no caso do JavaScript, até o próprio JavaScript pode ser ruim, porque no JavaScript você pode sobrescrever qualquer função e não pode ter certeza de que está usando a real; se um código malicioso iniciar antes de você, você não poderá confiar em nenhuma função interna do JavaScript: D

Agora, o epílogo deste post é:

Se você REALMENTE precisar dele (80% do tempo de avaliação NÃO é necessário) e tiver certeza do que está fazendo, basta usar eval (ou melhor Função;)), fechamentos e POO cobrem 80/90% do caso o eval possa ser substituído usando outro tipo de lógica, o restante será um código gerado dinamicamente (por exemplo, se você estiver escrevendo um intérprete) e, como você já disse, avaliando o JSON (aqui você pode usar a avaliação segura de Crockford;))

kentaromiura
fonte
E, como apontado pelo próprio Crockford , os navegadores atuais têm uma função interna JSON.parse .
Ruud Helderman
4

O Eval é complementar à compilação, que é usada na modelagem do código. Por modelo, quero dizer que você escreve um gerador de modelos simplificado que gera código de modelo útil que aumenta a velocidade de desenvolvimento.

Eu escrevi uma estrutura em que os desenvolvedores não usam EVAL, mas eles usam nossa estrutura e, por sua vez, essa estrutura precisa usar EVAL para gerar modelos.

O desempenho de EVAL pode ser aumentado usando o seguinte método; em vez de executar o script, você deve retornar uma função.

var a = eval("3 + 5");

Deve ser organizado como

var f = eval("(function(a,b) { return a + b; })");

var a = f(3,5);

O armazenamento em cache f certamente melhorará a velocidade.

Além disso, o Chrome permite a depuração de tais funções com muita facilidade.

Em relação à segurança, usar eval ou não dificilmente fará diferença,

  1. Primeiro de tudo, o navegador chama o script inteiro em uma caixa de areia.
  2. Qualquer código que é mau no EVAL, é mau no próprio navegador. O invasor ou qualquer pessoa pode injetar facilmente um nó de script no DOM e fazer qualquer coisa se puder avaliar alguma coisa. Não usar EVAL não fará nenhuma diferença.
  3. A maior parte da segurança do lado do servidor é prejudicial. A validação incorreta de cookies ou a implementação incorreta da ACL no servidor causam a maioria dos ataques.
  4. Uma vulnerabilidade recente do Java etc. estava presente no código nativo do Java. O JavaScript foi e foi projetado para ser executado em uma caixa de proteção, enquanto os applets foram projetados para serem executados fora de uma caixa de proteção com certificados etc., que levam a vulnerabilidades e muitas outras coisas.
  5. Escrever código para imitar um navegador não é difícil. Tudo o que você precisa fazer é fazer uma solicitação HTTP para o servidor com sua string de agente de usuário favorita. Todas as ferramentas de teste zombam dos navegadores de qualquer maneira; se um invasor quiser prejudicá-lo, EVAL é seu último recurso. Eles têm muitas outras maneiras de lidar com a segurança do servidor.
  6. O DOM do navegador não tem acesso a arquivos e nem um nome de usuário. De fato, nada na máquina a que avaliação pode dar acesso.

Se a segurança do servidor for sólida o suficiente para qualquer um atacar de qualquer lugar, não se preocupe com o EVAL. Como mencionei, se o EVAL não existir, os invasores terão muitas ferramentas para invadir seu servidor, independentemente da capacidade de EVAL do seu navegador.

O Eval é bom apenas para gerar alguns modelos para executar um processamento complexo de strings com base em algo que não é usado previamente. Por exemplo, eu vou preferir

"FirstName + ' ' + LastName"

Ao contrário de

"LastName + ' ' + FirstName"

Como meu nome de exibição, que pode vir de um banco de dados e que não é codificado.

Akash Kava
fonte
Você pode usar a função em vez de eval - function (first, last) { return last + ' ' + first }.
Konrad Borowski
Os nomes das colunas vêm do banco de dados.
Akash Kava
3
A ameaça de evalé principalmente outros usuários . Digamos que você tenha uma página de configurações e permite definir como o seu nome será exibido para outras pessoas. Digamos também que você não estava pensando muito claramente quando o escreveu, portanto, sua caixa de seleção tem opções como <option value="LastName + ' ' + FirstName">Last First</option>. Abro minhas ferramentas de desenvolvimento, altero a valueopção de alert('PWNED!'), seleciono a opção alterada e envio o formulário. Agora, sempre que outra pessoa puder ver meu nome de exibição, esse código será executado.
cHao
@cHao, O que você está falando é um exemplo de segurança ruim do servidor, o servidor nunca deve aceitar dados que possam ser executados como código no navegador de alguém. Mais uma vez, você não conseguiu entender o conceito de segurança ruim do lado do servidor.
Akash Kava
1
Você pode reclamar da segurança do servidor, se quiser, mas o objetivo principal evalé executar código que não faz parte do script que você escreveu. Se você não precisa do poder para fazer isso (e quase nunca o faz), evitar evalajuda a afastar uma categoria inteira de problemas. Isso é bom se o código do servidor não for perfeito.
cHao 12/06
4

Ao depurar no Chrome (v28.0.1500.72), descobri que as variáveis ​​não estão vinculadas aos fechamentos se não forem usadas em uma função aninhada que produz o fechamento. Eu acho que é uma otimização do mecanismo JavaScript.

MAS : quando eval()é usado dentro de uma função que causa um fechamento, TODAS as variáveis ​​de funções externas são vinculadas ao fechamento, mesmo que não sejam usadas. Se alguém tiver tempo para testar se é possível produzir vazamentos de memória, deixe um comentário abaixo.

Aqui está o meu código de teste:

(function () {
    var eval = function (arg) {
    };

    function evalTest() {
        var used = "used";
        var unused = "not used";

        (function () {
            used.toString();   // Variable "unused" is visible in debugger
            eval("1");
        })();
    }

    evalTest();
})();

(function () {
    var eval = function (arg) {
    };

    function evalTest() {
        var used = "used";
        var unused = "not used";

        (function () {
            used.toString();   // Variable "unused" is NOT visible in debugger
            var noval = eval;
            noval("1");
        })();
    }

    evalTest();
})();

(function () {
    var noval = function (arg) {
    };

    function evalTest() {
        var used = "used";
        var unused = "not used";

        (function () {
            used.toString();    // Variable "unused" is NOT visible in debugger
            noval("1");
        })();
    }

    evalTest();
})();

O que eu gostaria de destacar aqui é que eval () não deve necessariamente se referir à eval()função nativa . Tudo depende do nome da função . Portanto, ao chamar o nativo eval()com um nome alternativo (digamos var noval = eval;e depois em uma função interna noval(expression);), a avaliação de expressionpode falhar quando se refere a variáveis ​​que devem fazer parte do fechamento, mas na verdade não são.

Benjamin
fonte
3

Bottom Line

Se você criou ou higienizou o código eval, nunca é mau .

Um pouco mais detalhado

evalé ruim se estiver executando no servidor usando a entrada enviada por um cliente que não foi criado pelo desenvolvedor ou que não foi higienizado pelo desenvolvedor .

evalnão é ruim se estiver sendo executado no cliente, mesmo se você estiver usando entradas não autorizadas criadas pelo cliente .

Obviamente, você deve sempre limpar a entrada, para ter algum controle sobre o que seu código consome.

Raciocínio

O cliente pode executar qualquer código arbitrário que desejar, mesmo que o desenvolvedor não tenha codificado; Isso é verdade não apenas para o que é avaliado, mas para o chamado a evalsi mesmo .

Steven Spungin
fonte
2

A única instância em que você deve usar eval () é quando você precisa executar JS dinâmico em tempo real. Estou falando de JS que você baixa assincronamente do servidor ...

... E 9 vezes em 10, você poderia facilmente evitar isso refatorando.

Oli
fonte
Hoje em dia, existem outras (e melhores) maneiras de carregar o JavaScript de forma assíncrona a partir do servidor: w3bits.com/async-javascript
Ruud Helderman
1

evalraramente é a escolha certa. Embora possa haver inúmeras instâncias em que você pode realizar o que precisa realizando concatenando um script e executando-o em tempo real, normalmente você tem técnicas muito mais poderosas e de manutenção à sua disposição: notação de matriz associativa ( obj["prop"]é a mesma que obj.prop) , fechamentos, técnicas orientadas a objetos, técnicas funcionais - use-as.

yfeldblum
fonte
1

No que diz respeito ao script do cliente, acho que a questão da segurança é um ponto discutível. Tudo o que é carregado no navegador está sujeito a manipulação e deve ser tratado como tal. Não há risco zero no uso de uma instrução eval () quando existem maneiras muito mais fáceis de executar o código JavaScript e / ou manipular objetos no DOM, como a barra de URL no seu navegador.

javascript:alert("hello");

Se alguém quiser manipular seu DOM, eu digo afaste-se. A segurança para impedir qualquer tipo de ataque deve sempre ser de responsabilidade do aplicativo do servidor, ponto final.

Do ponto de vista pragmático, não há benefício em usar um eval () em uma situação em que as coisas podem ser feitas de outra maneira. No entanto, existem casos específicos em que uma avaliação DEVE ser usada. Nesse caso, definitivamente pode ser feito sem nenhum risco de explodir a página.

<html>
    <body>
        <textarea id="output"></textarea><br/>
        <input type="text" id="input" />
        <button id="button" onclick="execute()">eval</button>

        <script type="text/javascript">
            var execute = function(){
                var inputEl = document.getElementById('input');
                var toEval = inputEl.value;
                var outputEl = document.getElementById('output');
                var output = "";

                try {
                    output = eval(toEval);
                }
                catch(err){
                    for(var key in err){
                        output += key + ": " + err[key] + "\r\n";
                    }
                }
                outputEl.value = output;
            }
        </script>
    <body>
</html>
Peter Mortensen
fonte
6
Re "Não há risco zero no uso de uma instrução eval () quando existem maneiras muito mais fáceis de executar javascript e / ou manipular objetos no DOM". A injeção de código é um problema quando um usuário pode inserir um código que é executado no navegador de outro usuário. Os consoles do navegador por si só não permitem que um usuário execute o código em outro navegador, portanto, são irrelevantes ao decidir se vale a pena proteger contra a injeção de código.
Mike Samuel
Não é <head></head>necessário, mesmo que vazio?
Peter Mortensen
2
Esta resposta ignora completamente os riscos do XSS .
Ruud Helderman
1

No lado do servidor, eval é útil quando se lida com scripts externos, como sql ou influxdb ou mongo. Onde a validação personalizada em tempo de execução pode ser feita sem reimplementar seus serviços.

Por exemplo, um serviço de conquista com os seguintes metadados

{
  "568ff113-abcd-f123-84c5-871fe2007cf0": {
    "msg_enum": "quest/registration",
    "timely": "all_times",
    "scope": [
      "quest/daily-active"
    ],
    "query": "`SELECT COUNT(point) AS valid from \"${userId}/dump/quest/daily-active\" LIMIT 1`",
    "validator": "valid > 0",
    "reward_external": "ewallet",
    "reward_external_payload": "`{\"token\": \"${token}\", \"userId\": \"${userId}\", \"amountIn\": 1, \"conversionType\": \"quest/registration:silver\", \"exchangeProvider\":\"provider/achievement\",\"exchangeType\":\"payment/quest/registration\"}`"
  },
  "efdfb506-1234-abcd-9d4a-7d624c564332": {
    "msg_enum": "quest/daily-active",
    "timely": "daily",
    "scope": [
      "quest/daily-active"
    ],
    "query": "`SELECT COUNT(point) AS valid from \"${userId}/dump/quest/daily-active\" WHERE time >= '${today}' ${ENV.DAILY_OFFSET} LIMIT 1`",
    "validator": "valid > 0",
    "reward_external": "ewallet",
    "reward_external_payload": "`{\"token\": \"${token}\", \"userId\": \"${userId}\", \"amountIn\": 1, \"conversionType\": \"quest/daily-active:silver\", \"exchangeProvider\":\"provider/achievement\",\"exchangeType\":\"payment/quest/daily-active\"}`"
  }
}

Que então permitem,

  • Injeção direta de objetos / valores através de uma string literal em um json, útil para modelar textos

  • Pode ser usado como um comparador, digamos que criamos regras para validar missões ou eventos no CMS

Con disso:

  • Pode haver erros no código e interromper as coisas no serviço, se não for totalmente testado.

  • Se um hacker pode escrever um script no seu sistema, você está praticamente ferrado.

  • Uma maneira de validar seu script é manter o hash dos scripts em algum lugar seguro, para que você possa verificá-los antes da execução.

MichaelC
fonte
Agradável. Quando fiz a pergunta, eu nem pensava no JS do lado do servidor.
Richard Turner
1

Eu acho que qualquer caso de avaliação sendo justificado seria raro. É mais provável que você o use pensando que é justificado do que quando for realmente justificado.

Os problemas de segurança são os mais conhecidos. Mas saiba também que o JavaScript usa a compilação JIT e isso funciona muito mal com o eval. O Eval é um pouco como uma caixa preta para o compilador, e o JavaScript precisa prever o código com antecedência (até certo ponto) para aplicar com segurança e corretamente otimizações e escopo de desempenho. Em alguns casos, o impacto no desempenho pode até afetar outro código fora da avaliação.

Se você quiser saber mais: https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20%26%20closures/ch2.md#eval

Andre O
fonte
0

Não há problema em usá-lo se você tiver controle total sobre o código que é passado para a evalfunção.

John Topley
fonte
2
Se você tem total controle sobre o que está passando eval, a grande questão é: quando faz sentido que isso seja uma cadeia de caracteres em vez de um JS real?
precisa saber é
@cHao Por exemplo, se você tem um aplicativo de jogo grande (5 a 10 MB Javascript), é melhor criar primeiro um AJAX-Preloader simples (1kb) de carregamento rápido, que carrega o script principal grande, enquanto exibe um Bar ou algo semelhante. Após o download, você pode usar "eval (fonte)" ou melhor "nova função (fonte)" para executar o Game-Application-Script carregado. Dessa forma, o usuário pode ver visualmente que o aplicativo precisa de tempo para fazer o download até o jogo iniciar. Sem isso, o usuário precisa aguardar o carregamento de todo o aplicativo sem nenhum feedback visual.
SammieFox 11/04/19
@ SammieFox Existem outras (e melhores) maneiras de fazer isso, principalmente <script async="true" src="...">. Veja também: w3bits.com/async-javascript
Ruud Helderman
A resposta é um conselho perigoso; muitos desenvolvedores têm um falso senso de estar no controle. O conselho faz algum sentido para o software que não é mais mantido ativamente. Mas esse software deve ser considerado morto.
Ruud Helderman
0

Somente durante o teste, se possível. Observe também que eval () é muito mais lento que outros avaliadores especializados em JSON etc.

Eric Wendelin
fonte
0

Não há razão para não usar eval (), desde que você tenha certeza de que a fonte do código vem de você ou do usuário real. Embora ele possa manipular o que é enviado para a função eval (), isso não é um problema de segurança, porque ele é capaz de manipular o código fonte do site e, portanto, pode alterar o próprio código JavaScript.

Então ... quando não usar eval ()? Eval () não deve ser usado apenas quando houver uma chance de terceiros alterá-lo. Como interceptar a conexão entre o cliente e o servidor (mas se houver algum problema, use HTTPS). Você não deve avaliar () para analisar o código que é escrito por outras pessoas, como em um fórum.

Georg Schölly
fonte
Re "Não há razão para não usar eval (), desde que você tenha certeza de que a fonte do código vem de você ou do usuário real." Isso pressupõe que haja um único usuário. Essa premissa não está declarada no PO. Quando há vários usuários, o descuido evalde uma sequência composta de conteúdo de um usuário pode permitir que ele execute código no navegador de outro usuário.
Mike Samuel
@ MikeSamuel, eval pode executar código no navegador de outros usuários, eu não ouvi isso, você já tentou isso? Isso nunca aconteceu no histórico de navegação, você pode nos mostrar um exemplo?
Akash Kava
@AkashKava, Uma string pode se originar com um user-agent, ser armazenada em um banco de dados e depois ser veiculada em outro navegador eval. Isso acontece o tempo todo.
19413 Mike13
@MikeSamuel database? Onde? quem serve a corda errada? não é o banco de dados no lado do servidor a culpa? Antes de mais nada, o EVAL não deve ser responsabilizado pelo código do lado do servidor mal escrito. Use jsfiddle e mostre ao mundo um exemplo do mundo real onde ele pode causar danos.
Akash Kava
2
@AkashKava, não entendi sua pergunta. Não estamos falando de um aplicativo específico, mas de motivos para não usá-lo eval. Como é útil culpar o servidor? Se alguém deve ser responsabilizado, deve ser o atacante. Independentemente da culpa, um cliente que não é vulnerável ao XSS, apesar dos erros no servidor, é melhor que um cliente vulnerável, sendo o resto igual.
19613 Mike Mike
0

Se é realmente necessário, avaliar não é mau. Mas 99,9% dos usos de eval que eu tropeço não são necessários (não incluindo coisas de setTimeout).

Para mim, o mal não é uma performance ou mesmo um problema de segurança (bem, indiretamente, são os dois). Todos esses usos desnecessários de eval contribuem para um inferno de manutenção. Ferramentas de refatoração são descartadas. A pesquisa de código é difícil. Os efeitos imprevisíveis dessas avaliações são numerosos.

PEZ
fonte
5
eval não é necessário para setTimeout. Você também pode usar uma referência de função.
Matthew Crumley
0

Quando o eval () do JavaScript não é mau?

Estou sempre tentando desencorajar o uso de eval . Quase sempre, está disponível uma solução mais limpa e sustentável. O Eval não é necessário mesmo para a análise JSON . Eval contribui para o inferno da manutenção . Não sem razão, é desaprovado por mestres como Douglas Crockford.

Mas encontrei um exemplo em que deveria ser usado:

Quando você precisa passar a expressão.

Por exemplo, eu tenho uma função que constrói um google.maps.ImageMapTypeobjeto geral para mim, mas preciso informar a receita, como deve construir a URL do bloco a partir dos parâmetros zoome coord:

my_func({
    name: "OSM",
    tileURLexpr: '"http://tile.openstreetmap.org/"+b+"/"+a.x+"/"+a.y+".png"',
    ...
});

function my_func(opts)
{
    return new google.maps.ImageMapType({
        getTileUrl: function (coord, zoom) {
            var b = zoom;
            var a = coord;
            return eval(opts.tileURLexpr);
        },
        ....
    });
}
TMS
fonte
3
Parece que poderia ser refatorado para que eval () não seja necessário - tileURLexpr é apenas um modelo, portanto, algum uso criterioso de replace () faria o trabalho. Ainda assim, ele me lembra um exemplo que eu tinha em mente quando enviei a pergunta, que dizia respeito a permitir que um usuário especifique uma fórmula matemática a ser avaliada, semelhante à funcionalidade da planilha. Claro que não mencionei isso na época porque não queria influenciar as respostas!
Richard Turner
8
tileURL: function (zoom, coord) { return 'http://tile.openstreetmap.org/' + b + '/' + a.x + '/' + a.y + '.png'; },
Casey Chu
0

Meu exemplo de uso eval: import .

Como é geralmente feito.

var components = require('components');
var Button = components.Button;
var ComboBox = components.ComboBox;
var CheckBox = components.CheckBox;
...
// That quickly gets very boring

Mas com a ajuda evale uma pequena função auxiliar, ele fica com uma aparência muito melhor:

var components = require('components');
eval(importable('components', 'Button', 'ComboBox', 'CheckBox', ...));

importable pode parecer (esta versão não suporta a importação de membros concretos).

function importable(path) {
    var name;
    var pkg = eval(path);
    var result = '\n';

    for (name in pkg) {
        result += 'if (name !== undefined) throw "import error: name already exists";\n'.replace(/name/g, name);
    }

    for (name in pkg) {
        result += 'var name = path.name;\n'.replace(/name/g, name).replace('path', path);
    }
    return result;
}
Yaroslav
fonte
2
+1 para a idéia, mas você tem um bug aqui: .replace(/name/g, name).replace('path', path). Se namecontiver a string "path", você poderá obter surpresas.
wberry
1
Declarar uma variável para cada propriedade de componentsé um possível cheiro de código; refatorar seu código pode eliminar completamente o 'problema'. Sua solução atual é apenas açúcar sintático. Se você insistir em fazer isso, recomendo que você escreva seu próprio pré-processador, a ser executado antes da implantação. Isso deve ficar evallonge do código de produção.
Ruud Helderman
0

Eval não é mau, apenas mal utilizado.

Se você criou o código ou pode confiar nele, tudo bem. As pessoas continuam falando sobre como a entrada do usuário não importa com a avaliação. Bem, tipo de ~

Se houver uma entrada do usuário que vá para o servidor, ela retornará ao cliente e esse código será usado na avaliação sem ser higienizado. Parabéns, você abriu a caixa da pandora para que os dados do usuário sejam enviados para quem quer que seja.

Dependendo de onde a avaliação está, muitos sites usam SPAs, e a avaliação poderia facilitar o acesso do usuário a aplicativos internos que, de outra forma, não teriam sido fáceis. Agora eles podem criar uma extensão de navegador falsa que pode gravar nessa avaliação e roubar dados novamente.

Só preciso descobrir qual é o sentido de você usar a avaliação. Gerar código não é realmente ideal quando você pode simplesmente criar métodos para fazer esse tipo de coisa, usar objetos ou algo parecido.

Agora, um bom exemplo do uso de eval. Seu servidor está lendo o arquivo de arrogância que você criou. Muitos dos parâmetros de URL são criados no formato {myParam}. Portanto, você gostaria de ler os URLs e convertê-los em seqüências de caracteres de modelo sem precisar fazer substituições complexas, porque você tem muitos pontos de extremidade. Então você pode fazer algo assim. Observe que este é um exemplo muito simples.

const params = { id: 5 };

const route = '/api/user/{id}';
route.replace(/{/g, '${params.');

// use eval(route); to do something
jemiloii
fonte
-1

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 . O hiperescrito é gerado como uma string primeiro e, antes de devolvê-lo, eval()transforma-o em código executável. eu encontreieval() 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 também 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 .

Wikened
fonte
"O hiperescrito é gerado como uma cadeia de caracteres primeiro (...)" Faz mais sentido executar toda a geração de código na fase de construção, gravar o código de hiperescrito resultante em um arquivo executável (.js) separado e implantar esse arquivo para testar e Produção. Eu amo o jeito que você usa a geração de código. É apenas evaluma dica de que alguma responsabilidade que pertence no tempo de compilação se mudou para o tempo de execução.
Ruud Helderman
-1

Minha opinião é que eval é uma função muito poderosa para aplicativos Web do lado do cliente e é seguro ... Tão seguro quanto o JavaScript, que não é. :-) Os problemas de segurança são essencialmente um problema do lado do servidor porque, agora, com ferramentas como o Firebug, você pode atacar qualquer aplicativo JavaScript.

Peter Mortensen
fonte
1
O uso de evalprecisa ser protegido contra ataques XSS, o que nem sempre é fácil de corrigir.
Benjamin
-1

O Eval é útil para geração de código quando você não possui macros.

Por exemplo (estúpido), se você estiver escrevendo um compilador Brainfuck , provavelmente desejará construir uma função que execute a sequência de instruções como uma string e avaliar para retornar uma função.

Erik Haliewicz
fonte
Você escreve um compilador (que salva em vez de executar o código que está sendo gerado) ou escreve um intérprete (onde cada instrução tem uma implementação pré-compilada). Nem é um caso de uso para eval.
Ruud Helderman
Se você gerou o código javascript e quisesse executá-lo imediatamente (digamos, para obter benefícios de desempenho em detrimento da interpretação direta), esse seria um caso de uso para avaliação.
Erik Haliewicz 16/07/19
Bom ponto; Eu vi um exemplo neste artigo sobre Blockly . Fico chocado com o que o Google recomenda eval, quando a alternativa ( Função ) é mais rápida ( como explicada no MDN ) e mais confiável (evita erros imprevisíveis pelo melhor isolamento entre o código gerado e outro código 'de suporte' na mesma página da web).
Ruud Helderman
-5

Quando você analisa uma estrutura JSON com uma função de análise (por exemplo, jQuery.parseJSON), espera uma estrutura perfeita do arquivo JSON (cada nome de propriedade está entre aspas duplas). No entanto, o JavaScript é mais flexível. Portanto, você pode usar eval () para evitá-lo.

vitmalina
fonte
4
Não use cegamente eval, esp. ao obter dados JSON de uma fonte de terceiros. Consulte JSON.Stringify sem aspas nas propriedades? para a abordagem correta para analisar "JSON sem nomes de chave entre aspas".
22712 Rob Rob W
2
Se ele não usar aspas duplas em torno dos nomes de propriedades, pode ser uma representação em cadeia de caracteres de um objeto literal, mas não é JSON . JSON define os nomes das propriedades como stringae define a stringcomo uma sequência de zero ou mais caracteres Unicode, agrupados entre aspas duplas, usando escapes de barra invertida.
Código inútil
Ver artigo de Nikolas Zákas - "eval () não é mau, apenas mal compreendido" nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood
vitmalina
@vitmalina No artigo de Zakas: "Isso pode ser perigoso se você estiver usando a entrada do usuário e executando-a através de eval (). No entanto, se a sua entrada não for do usuário, existe algum perigo real?" Esse é exatamente o problema. Uma vez que seu código cresce além das proporções do 'olá mundo', torna-se rapidamente impossível provar que você não está vazando informações do usuário eval. Em qualquer aplicativo Web sério com vários inquilinos, com dezenas de desenvolvedores trabalhando na mesma base de código, isso é inaceitável.
Ruud Helderman