Porque não utilizar eval em JavaScript?

Neste artigo você vai entender porque não utilizar eval nos seus programas de JavaScript, e qual impacto essa função pode causar

Resposta rápida: o eval() executa uma string como código JavaScript, e esse código roda com as mesmas permissões da página ou extensão que chamou

Por isso a documentação de referência classifica executar JavaScript a partir de uma string como um risco de segurança enorme, e o problema tem classificação formal de vulnerabilidade: CWE-95, Eval Injection

Um ponto importante que quase ninguém diz: o eval não é obsoleto, ele continua fazendo parte do padrão ECMAScript e está documentado sem aviso de descontinuação

O que muda é o cuidado, não a disponibilidade

Na prática, são três defesas que funcionam de verdade:

  1. CSP com default-src ou script-src, que já desabilita por padrão o eval(), o argumento string do setTimeout e do setInterval e o construtor Function()
  2. No Node, a flag –disallow-code-generation-from-strings, que faz eval e new Function lançarem EvalError
  3. As regras no-eval e no-implied-eval do ESLint, pra o problema não nascer no repositório
Porque não utilizar eval capa

Fala programador(a), beleza? Bora aprender mais sobre o eval!

O uso da função eval() em JavaScript é considerado uma prática perigosa, pois permite que código malicioso seja executado no sistema de quem estiver acessando a página.

Isso acontece porque a função eval() executa uma string como código JavaScript, o que significa que qualquer código passado para ela será executado.

Isso pode ser perigoso se a string for fornecida por um usuário mal-intencionado, pois eles podem passar código malicioso para ser executado, como roubar informações sensíveis ou instalar software malicioso.

Além disso, o uso de eval() também pode tornar o seu código menos seguro, pois ele pode ser explorado por ataques de injeção de código.

Existem alternativas mais seguras ao uso de eval() para realizar tarefas semelhantes.

Por exemplo, a função JSON.parse() pode ser usada para avaliar uma string como um objeto JSON, enquanto a função setTimeout() ou setInterval() podem ser usadas para avaliar uma string como código JavaScript em um momento específico.

Além disso, o uso de uma biblioteca de análise sintática, como o Acorn ou o Esprima, pode ser uma alternativa mais segura.

Em resumo, o uso de eval() em JavaScript é considerado uma prática perigosa devido ao risco de ataques de injeção de código.

É importante evitar o uso de eval() sempre que possível e usar alternativas mais seguras, como JSON.parse(), setTimeout(), setInterval() ou uma biblioteca de análise sintática.

Deixe um comentário contando o que achou deste artigo 🙂

Formação Claude Code
Formação Recomendada

Formação Claude Code

Domine Claude Code do absoluto zero até o avançado

  • 114 aulas
  • 4 projetos
  • 9h 18min

Quer aprender mais sobre JavaScript? Confira o vídeo abaixo:

Conclusão

Chegamos ao fim do artigo sobre o porque não utilizar eval em JavaScript!

Em conclusão, o uso de eval() em JavaScript é uma prática que deve ser evitada devido aos riscos de segurança que ela traz.

Como alternativas mais seguras, é recomendado usar funções como JSON.parse(), setTimeout(), setInterval() ou uma biblioteca de análise sintática.

É importante lembrar que a segurança deve ser uma preocupação constante na programação, e evitar o uso de eval() é uma das formas de garantir que o código não possa ser explorado por ataques maliciosos.

Está buscando a sua primeira vaga como Programador? Confira o nosso treinamento completo clicando aqui.

Uma plataforma de cursos com projetos completos, comunidade de alunos e muito mais!

Há também o nosso canal no YouTube, com muito conteúdo gratuito de qualidade!

O eval() foi descontinuado?

Não

Ele continua fazendo parte do padrão ECMAScript e está documentado sem nenhum aviso de descontinuação

Ou seja: o problema não é disponibilidade, é segurança

A função está lá, funcionando, e vai continuar rodando no teu código

O que a documentação de referência diz é outra coisa: executar JavaScript a partir de uma string é um risco de segurança enorme, porque esse código roda com as permissões de quem chamou, a página ou a extensão

É bem diferente de "não usa porque parou de funcionar", beleza?

Isso tem nome: CWE-95, Eval Injection

O risco do eval não é lenda de blog, ele tem classificação formal de vulnerabilidade

É o CWE-95, "Improper Neutralization of Directives in Dynamically Evaluated Code", ou Eval Injection: entrada que não foi neutralizada indo parar dentro de uma chamada de avaliação dinâmica

E ele mora debaixo de uma categoria maior, o CWE-94, que trata de controle impróprio de geração de código

A mitigação recomendada pelo MITRE é bem direta: valide estritamente o dado, de preferência com uma lista de permissão dos valores específicos que tu aceita, rejeitando qualquer metacaractere de código

Repara na ordem: primeiro tu limita o que pode entrar, depois tu pensa em como executar

Se tu só consegue resolver o problema executando string de terceiro, o problema é outro 🙂

eval direto e eval indireto: qual a diferença?

Aqui tem um detalhe que quase nenhum artigo conta

Existem dois modos de chamar o eval, e eles se comportam diferente

O eval direto é a chamada literal, escrita assim mesmo, eval(…)

Ele enxerga o escopo local e é mais lento justamente por causa dessa inspeção de escopo

Qualquer outra forma de chamada é eval indireto: um alias, um acesso por membro, um optional chaining

E o indireto roda no escopo global

eval('var x = 1')      // direto, enxerga o escopo local
const run = eval
run('var y = 2')       // indireto, cai no escopo global

E tem mais um detalhe que pega muita gente: em strict mode, o código do eval usa um ambiente de variáveis próprio

Na prática isso quer dizer que ele não cria variáveis nem funções no escopo de quem chamou

Então se tu esperava que aquele var fosse aparecer na tua função, não vai

Tome cuidado com o setTimeout e o setInterval:

Esse é o erro mais comum, e ele passa despercebido porque parece inofensivo

setTimeout e setInterval aceitam uma string, e essa string é compilada e executada

Isso tem até nome: implied eval

A string é avaliada no escopo global, funciona como porta de entrada pra XSS e é bloqueada por CSP estrita, exatamente igual ao eval

A documentação desencoraja fortemente esse uso, pelos mesmos motivos

O certo é passar uma referência de função:

setTimeout(minhaFuncao, 1000)     // referência de função, tranquilo
setTimeout('minhaFuncao()', 1000) // implied eval, foge disso

Mesma função, mesma API, e a diferença toda está no tipo do primeiro argumento

Como bloquear o eval no navegador com CSP:

A melhor parte é que tu não precisa confiar na disciplina do time, dá pra desligar isso no nível do navegador

Uma Content Security Policy com default-src ou script-src já desabilita por padrão todas as APIs que transformam string em código

Cai tudo de uma vez: o eval(), o argumento string do setTimeout e do setInterval, e o construtor Function()

Quando alguma dessas rodar, o navegador reclama no DevTools com a mensagem "Content Security Policy of your site blocks the use of ‘eval’ in JavaScript"

Aí vem a tentação: existe a palavra-chave ‘unsafe-eval’, que reabilita a avaliação dinâmica de strings

E a própria documentação recomenda evitar, porque usar ela derruba boa parte do propósito de ter uma CSP

Se tu realmente precisa de avaliação dinâmica, existe a alternativa ‘trusted-types-eval’, que só libera quando Trusted Types estão sendo aplicados e valores tratados são passados no lugar de strings cruas

E no Node.js, dá pra desligar o eval?

O Node aceita a flag de V8 –disallow-code-generation-from-strings, inclusive via NODE_OPTIONS

Com ela ligada, eval e new Function param de funcionar e passam a lançar "EvalError: Code generation from strings disallowed for this context"

É um guardrail (limite) bem barato de colocar num serviço que nunca deveria gerar código em runtime

E já que a gente tá no assunto, um aviso que muita gente não leu: a documentação oficial do Node diz, com todas as letras, que o módulo node:vm não é um mecanismo de segurança

"The node:vm module is not a security mechanism. Do not use it to run untrusted code."

Então não adianta trocar o eval pelo vm achando que virou sandbox, não virou

Como o ESLint acha eval no teu código:

Regra que não é checada automaticamente é regra que ninguém segue, né?

O ESLint tem a regra no-eval, que alerta em qualquer uso de eval() e é descrita justamente como voltada a evitar código perigoso, desnecessário e lento

E tem a companheira dela, a no-implied-eval, que alerta quando setTimeout, setInterval ou execScript recebem string como primeiro argumento

A orientação da regra é sempre a mesma: passa função como primeiro argumento

Se o teu projeto é TypeScript, o typescript-eslint mantém a própria versão da no-implied-eval, que usa informação de tipos pra detectar o padrão

Duas regras ligadas e o problema para de nascer no teu repositório

Um adendo sobre as alternativas:

Vale separar as coisas, porque "alternativa ao eval" virou saco de gato

Se o que tu quer é ler dado, o JSON.parse resolve e resolve com segurança: ele apenas interpreta texto JSON e transforma em valor JavaScript

Se a entrada for inválida, ele lança um SyntaxError, e nenhum código da string é executado

É essa a diferença que importa: parse não é execução

Já o Acorn e o Esprima são bibliotecas de análise sintática, é outra caixa de ferramenta, pra outro trabalho

E se tu for escolher uma hoje, se liga no estado delas: o Esprima está estagnado, a última versão publicada no npm é a 4.0.1 e o pacote é classificado como sem manutenção ativa

Tanto que existe o fork esprima-next, criado justamente porque o Esprima parou de lançar versões

O Acorn segue ativo, com a versão 8.18.0 publicada recentemente, e com volume de downloads semanais na casa das centenas de milhões

Agora, o resumo honesto: se o teu caso é executar código que veio de fora, a resposta não é trocar de biblioteca, é não executar

Inserir a seção ‘## Perguntas frequentes sobre o eval em JavaScript’ com o conteúdo do campo geo.faq, cada pergunta como H3

Inserir o conteúdo do campo geo.primeiraMao como seção ‘## Por que eu tiro código de dentro de string:’, amarrando com o vídeo que já está embedado logo abaixo

Eu gravei um vídeo aqui pro canal sobre o javascript:void(0), aquele cara que fica dentro do href dos links em código antigo

E o que eu falo lá vale inteirinho aqui: eu prefiro tirar o código de dentro do HTML e resolver tudo no JavaScript, com addEventListener e preventDefault

No vídeo eu mostro os dois lados na prática, o link com void no atributo e a versão com o evento tratado no JS, e o resultado é o mesmo

Muda a manutenção

Eu deixo o HTML só pra tag, conteúdo e texto, e a lógica fica toda em um lugar só

Esse é exatamente o padrão mental que eu levo pro eval: código é código, string é dado, e é na hora que tu mistura os dois que a coisa quebra

Quando a lógica vive dentro de uma string, seja num atributo HTML, seja no primeiro argumento de um setTimeout, tu perde tudo o que a ferramenta te dá: o editor não completa, o linter não enxerga, e o navegador com CSP simplesmente não deixa rodar

O que eu ainda quero testar e gravar: subir uma página com script-src ligado pra mostrar a violação aparecendo no DevTools, e rodar um script Node com a flag que desliga geração de código a partir de strings pra ver o EvalError na cara

Quando eu fizer, eu volto aqui e atualizo o post =)

Perguntas frequentes

O que a função eval() faz em JavaScript?

Ela recebe uma string e executa o conteúdo dessa string como código JavaScript

O detalhe que importa: esse código roda com as permissões de quem chamou, a página ou a extensão

É por isso que a documentação de referência trata executar JavaScript a partir de uma string como um risco de segurança enorme

O eval() é deprecated ou foi removido do JavaScript?

Não

Ele continua fazendo parte do padrão ECMAScript e está documentado sem nenhum aviso de descontinuação

O problema dele é de segurança, não de disponibilidade

Qual a diferença entre eval direto e eval indireto?

O eval direto é a chamada literal, eval(…) escrita assim mesmo: ele enxerga o escopo local e é mais lento por causa da inspeção de escopo

Qualquer outra forma de chamada, seja por alias, por acesso de membro ou com optional chaining, é eval indireto

E o indireto roda no escopo global

O eval funciona diferente em strict mode?

Sim

Em strict mode, o código do eval usa um ambiente de variáveis próprio

Ou seja, ele não cria variáveis nem funções no escopo de quem chamou, em vez de instanciar tudo lá fora

Passar uma string pro setTimeout é a mesma coisa que usar eval?

Na prática, sim, e isso tem nome: implied eval

setTimeout e setInterval aceitam uma string que é compilada e executada, avaliada no escopo global, funcionando como porta de entrada pra XSS

É bloqueada por CSP estrita e fortemente desencorajada pela documentação

O correto é passar uma referência de função como primeiro argumento

Como bloquear o uso de eval no navegador?

Com uma Content Security Policy

Uma CSP com default-src ou script-src já desabilita por padrão todas as APIs que transformam string em código: o eval(), o argumento string do setTimeout e do setInterval e o construtor Function()

Quando algo tenta rodar, o DevTools mostra a mensagem "Content Security Policy of your site blocks the use of ‘eval’ in JavaScript"

Posso liberar o eval na CSP com unsafe-eval?

Dá pra liberar, mas a recomendação é evitar

A palavra-chave ‘unsafe-eval’ reabilita a avaliação dinâmica de strings, e a documentação afirma que usar ela derruba boa parte do propósito de ter uma CSP

Se tu precisa mesmo de avaliação dinâmica, existe a alternativa ‘trusted-types-eval’, que só permite quando Trusted Types estão sendo aplicados e valores tratados são passados no lugar de strings cruas

Como desativar o eval no Node.js?

Rodando o processo com a flag de V8 –disallow-code-generation-from-strings, que também pode ir via NODE_OPTIONS

Com ela, eval e new Function passam a lançar "EvalError: Code generation from strings disallowed for this context"

O módulo node:vm serve como sandbox pra rodar código não confiável?

Não

A documentação oficial do Node é literal nisso: "The node:vm module is not a security mechanism. Do not use it to run untrusted code."

Trocar eval por node:vm não transforma o teu código em sandbox

Qual regra do ESLint pega o uso de eval?

São duas

A no-eval alerta em qualquer uso de eval(), e é descrita como voltada a evitar código perigoso, desnecessário e lento

A no-implied-eval alerta quando setTimeout, setInterval ou execScript recebem string como primeiro argumento

Em projeto TypeScript, o typescript-eslint mantém a própria versão da no-implied-eval, que usa informação de tipos pra detectar o padrão

JSON.parse é uma alternativa segura ao eval?

Pra ler dado, sim

O JSON.parse apenas interpreta texto JSON e converte em valor JavaScript

Se a entrada for inválida, ele lança um SyntaxError, e nenhum código da string é executado

A diferença é essa: parse não é execução

Acorn e Esprima substituem o eval?

Elas são bibliotecas de análise sintática, é outra ferramenta pra outro trabalho

Se tu for escolher uma, olha o estado delas: o Esprima está estagnado, com a 4.0.1 como última versão publicada no npm e manutenção avaliada como inativa, tanto que existe o fork esprima-next criado justamente por causa da ausência de novos lançamentos

O Acorn segue ativo, na versão 8.18.0, com downloads semanais na casa das centenas de milhões

Leia também

Escrito por | Matheus Battisti

Matheus Battisti
Fundador da Hora de Codar

Programador apaixonado pelo mundo das tecnologias, sempre buscando em aprender e se aprofundar em linguagens, frameworks e o que mais for necessário para executar um bom trabalho. Agora tem uma nova missão que é de passar seu conhecimento adiante para formar novos programadores e especializar mais os que já são.

Subscribe
Notify of
guest

0 Comentários
Oldest
Newest Most Voted

Formações

Formação Vibe Coding

Formação Vibe Coding

Do Prompt ao Produto: Crie Software Real com IA

  • 474 aulas
  • 20 projetos
  • 39h 27min

Blog | Mais populares