A maioria das línguas modernas (que são de alguma forma interpretadas) têm algum tipo de eval função. Essa função executa código de idioma arbitrário, na maioria das vezes passado como argumento principal como uma string (idiomas diferentes podem adicionar mais recursos à função eval).
Entendo que os usuários não devem ter permissão para executar essa função ( editar, ou seja, receber diretamente ou indiretamente informações arbitrárias de um usuário arbitrário para serem repassados eval
), especialmente com o software do servidor, pois podem forçar o processo a executar código malicioso. Dessa forma, tutoriais e comunidades nos dizem para não usar o eval. No entanto, há muitas vezes em que eval é útil e usado:
- Regras de acesso personalizadas para elementos de software (IIRC OpenERP possui um objeto
ir.rule
que pode usar código python dinâmico). - Cálculos e / ou critérios personalizados (o OpenERP possui campos como esse para permitir cálculos de código personalizados).
- Analisadores de relatórios do OpenERP (sim, eu sei que estou assustando você com coisas do OpenERP ... mas é o principal exemplo que tenho).
- Codificando efeitos de feitiço em alguns jogos de RPG.
Portanto, eles têm um bom uso, desde que sejam usados corretamente. A principal vantagem é que o recurso permite que os administradores escrevam códigos personalizados sem precisar criar mais arquivos e incluí-los (embora a maioria das estruturas que usam os recursos eval também tenham uma maneira de especificar um arquivo, módulo, pacote, ... para ler).
No entanto, eval é mau na cultura popular. Coisas como invadir seu sistema vêm à mente.
No entanto, existem outras funções que podem ser prejudiciais se, de alguma forma, forem acessadas pelos usuários: desvincular, ler, escrever (semântica de arquivos), alocação de memória e aritmética de ponteiros, acesso ao modelo de banco de dados (mesmo que não considerando casos injetáveis por SQL).
Portanto, basicamente, na maioria das vezes, quando qualquer código não é gravado corretamente ou não é monitorado adequadamente (recursos, usuários, ambientes, ...), o código é ruim e pode levar até a um impacto econômico.
Mas há algo especial com eval
funções (independentemente do idioma).
Pergunta : Existe algum fato histórico para esse medo se tornar parte da cultura popular, em vez de dar a mesma atenção aos outros aspectos possivelmente perigosos?
fonte
eval
, ele possui uma função interna chamadasafe_eval
que prepara o ambiente para impedir que o código faça coisas perigosas. Porém, foram encontrados erros, já que o Python é uma linguagem bastante flexível e, portanto, difícil de controlar.Respostas:
Uma
eval
função por si só não é má, e há um ponto sutil que eu não acredito que você esteja fazendo:Permitir que um programa execute entradas arbitrárias do usuário é ruim
Eu escrevi um código que usava um
eval
tipo de função e era seguro: o programa e os parâmetros eram codificados. Às vezes, não há recurso de linguagem ou biblioteca para fazer o que o programa precisa e executar um comando shell é o caminho curto. "Eu tenho que terminar de codificar isso em algumas horas, mas escrever Java / .NET / PHP / qualquer código levará dois dias. Ou possoeval
em cinco minutos."Depois de permitir que os usuários executem o que quiserem, mesmo se bloqueados por privilégios de usuário ou atrás de uma tela "segura", você cria vetores de ataque. Toda semana, algum CMS aleatório, software de blog etc. tem uma falha de segurança corrigida, na qual um invasor pode explorar uma falha como essa. Você está confiando na pilha de software inteira para proteger o acesso a uma função que pode ser usada
rm -rf /
ou algo catastrófico (nota: é improvável que esse comando seja bem-sucedido, mas falhará após causar alguns danos).Sim, existe um precedente histórico. Devido aos inúmeros bugs que foram corrigidos ao longo dos anos em vários softwares que permitem que atacantes remotos executem códigos arbitrários, a idéia de
eval
cair fora de moda. As linguagens e bibliotecas modernas têm um rico conjunto de funcionalidades que se tornameval
menos importantes, e isso não é por acaso. Isso facilita o uso das funções e reduz o risco de uma exploração.Tem sido dada muita atenção a muitos recursos potencialmente inseguros em idiomas populares. Se alguém recebe mais atenção é principalmente uma questão de opinião, mas os
eval
recursos certamente têm um problema de segurança comprovável que é fácil de entender. Por um lado, eles permitem executar comandos do sistema operacional, incluindo shell embutidos e programas externos que são padrão (por exemplo,rm
oudel
). Dois, combinados com outras explorações, um invasor pode ser capaz de carregar seu próprio script executável ou shell e executá-lo através do seu software, abrindo a porta para que quase tudo aconteça (nada disso é bom).Este é um problema difícil. O software é complexo e uma pilha de softwares (por exemplo, LAMP ) é composta por vários softwares que interagem entre si de maneiras complexas. Cuidado ao usar recursos de linguagem como esse e nunca permita que os usuários executem comandos arbitrários.
fonte
eval
pode elevar privilégios. o que não é um problema se eles já estiverem elevados.rm -rf /
, não há necessidade de fazer isso de maneira complicada por meio de um IDE. O problemaeval
é que ele abre essa capacidade para muitos atores que não deveriam ter essa capacidade.Parte disso é simplesmente que as nuances são difíceis. É fácil dizer algo como nunca usar goto, campos públicos, interpolação de strings para consultas sql ou eval. Essas declarações não devem realmente ser entendidas como dizendo que nunca há, sob nenhuma circunstância, uma razão para usá-las. Mas evitá-los como regra geral é uma boa idéia.
O Eval está fortemente desencorajado porque combina vários problemas comuns.
Em primeiro lugar, é suscetível a ataques de injeção. Aqui é como a injeção de SQL, pois quando dados controlados pelo usuário são inseridos no código, é fácil permitir acidentalmente que código arbitrário seja inserido.
Em segundo lugar, os iniciantes tendem a usar eval para contornar códigos mal estruturados. Um codificador iniciante pode escrever um código parecido com:
Isso funciona, mas é realmente a maneira errada de resolver esse problema. Obviamente, usar uma lista seria muito melhor.
Em terceiro lugar, a avaliação é tipicamente ineficiente. Muito esforço é gasto para acelerar nossas implementações de linguagem de programação. Mas é difícil acelerar o eval e usá-lo normalmente terá efeitos prejudiciais no seu desempenho.
Então, eval não é mau. Podemos dizer que avaliar é mau, porque bem, é uma maneira cativante de colocá-lo. Qualquer programador iniciante deve ficar estritamente longe de eval, porque o que quer que eles desejem fazer, eval é quase certamente a solução errada. Para certos casos de uso avançados, eval faz sentido, e você deve usá-lo, mas obviamente tenha cuidado com as armadilhas.
fonte
var x = 3; print(x)
for compilado , não será necessário saber em tempo de execução que a fonte usou o nome "x". Paravar x = 3; print(eval("x"))
funcionar, esse mapeamento precisa ser registrado. Esta é uma questão real: no Common Lisp(let ((x 3)) (print (eval 'x)))
lançará uma exceção de variável não acoplada porque a variável lexical x não tem nenhuma conexão com o nome após a compilação do código.O que se resume é que a "execução arbitrária de código" é uma conversa técnica para "capaz de fazer qualquer coisa". Se alguém é capaz de explorar a execução arbitrária de código no seu código, esta é literalmente a pior vulnerabilidade de segurança possível, porque significa que eles podem fazer o que for possível para o seu sistema.
"Outros bugs possivelmente prejudiciais" podem ter limites, o que significa que eles são, por definição, capazes de causar menos danos do que uma execução arbitrária de código sendo explorada.
fonte
eval
são.Há uma razão prática e teórica.
A razão prática é que observamos que freqüentemente causa problemas. É raro que a avaliação resulte em uma boa solução, e muitas vezes resulta em uma solução ruim, onde você teria uma solução melhor no final se você fingisse que a avaliação não existia e abordasse o problema de maneira diferente. Portanto, o conselho simplificado é ignorá-lo e, se você encontrar um caso em que deseja ignorar o conselho simplificado, esperamos que você tenha pensado nisso o suficiente para entender por que o conselho simplificado não é aplicável e o comum armadilhas não afetarão você.
A razão mais teórica é que, se é difícil escrever um bom código, é ainda mais difícil escrever um código que escreva um bom código. Esteja você usando eval ou gerando instruções SQL juntando cadeias ou escrevendo um compilador JIT, o que você está tentando é geralmente mais difícil do que o esperado. O potencial de injeção de código mal-intencionado é uma grande parte do problema, mas, além disso, é geralmente mais difícil saber se o seu código está correto se ele nem existir até o tempo de execução. Portanto, o conselho simplificado é manter as coisas mais fáceis para você: "use consultas SQL parametrizadas", "não use eval".
Tomando seu exemplo de efeitos ortográficos: uma coisa é criar um compilador ou intérprete Lua (ou qualquer outro) em seu jogo, a fim de permitir aos designers de jogos uma linguagem mais fácil que C ++ (ou qualquer outro) para descrever os efeitos ortográficos. A maioria dos "problemas de avaliação" não se aplica se tudo o que você está fazendo é avaliar o código que foi escrito e testado e incluído no jogo ou no DLC ou o que você tem. Isso é apenas misturar idiomas. Os grandes problemas atingem você quando você tenta gerar Lua (ou C ++, ou SQL, ou comandos de shell, ou qualquer outra coisa) em tempo real e estraga tudo.
fonte
Não, não há fato histórico óbvio.
Os males de eval são fáceis de ver desde o início. Outros recursos são levemente perigosos. As pessoas podem excluir dados. As pessoas podem ver dados que não deveriam. As pessoas podem escrever dados que não deveriam. E eles só podem fazer a maioria dessas coisas se você errar de alguma forma e não validar a entrada do usuário.
Com o eval, eles podem invadir o pentágono e fazer parecer que você fez isso. Eles podem inspecionar suas teclas para obter suas senhas. Assumindo uma linguagem completa de Turing, eles podem literalmente fazer qualquer coisa que seu computador seja capaz de fazer.
E você não pode validar a entrada. É uma string arbitrária de forma livre. A única maneira de validar isso seria criar um analisador e um mecanismo de análise de código para o idioma em questão. Boa sorte com isso.
fonte
Eu acho que tudo se resume aos seguintes aspectos:
Necessidade
O que estou tentando dizer é: Você precisa ler / escrever para quase as ferramentas básicas. O mesmo para armazenar as pontuações mais altas do seu jogo tão incrível.
Sem leitura / gravação, seu editor de imagens é inútil. Sem
eval
? Vou escrever um plugin personalizado para isso!Uso (Guardado)
Muitos métodos podem ser potencialmente perigosos. Como, por exemplo, o
read
ewrite
. Um exemplo comum é um serviço da Web que permite ler imagens de um diretório específico, especificando o nome. No entanto, o 'nome' pode ser de fato qualquer caminho (relativo) válido no sistema, permitindo que você leia todos os arquivos aos quais o serviço da Web tem acesso, e não apenas as imagens. Abusar deste exemplo simples é chamado de 'caminho atravessado'. Se o seu aplicativo permite o percurso, é ruim. Umread
sem se defender contra o caminho pode ser chamado de mal.No entanto, em outros casos, a cadeia de caracteres
read
está totalmente sob o controle dos programadores (talvez codificado permanentemente?). Nesse caso, dificilmente é mau usarread
.Acesso
Agora, outro exemplo simples, usando
eval
.Em algum lugar do seu aplicativo da web, você deseja algum conteúdo dinâmico. Você permitirá que os administradores insiram algum código que seja executável. Como os administradores são usuários confiáveis, teoricamente isso pode ser aceitável. Certifique-se de não executar o código enviado por não administradores, e você está bem.
(Isto é, até que você despediu o bom administrador, mas esqueceu de revogar o acesso dele. Agora, seu aplicativo da Web está no lixo).
Verificabilidade.
Outro aspecto importante, eu acho, é como é fácil verificar a entrada do usuário.
Usando a entrada do usuário em uma chamada de leitura? Apenas certifique-se (muito) de que a entrada para a chamada de leitura não contenha nada de malicioso. Normalize o caminho e verifique se o arquivo aberto está no seu diretório de mídia. Agora isso é seguro.
Entrada do usuário em uma chamada de gravação? Mesmo!
Injeção SQL? Apenas escape ou use consultas parametrizadas e você estará seguro.
Eval? Como você vai verificar a entrada usada para a
eval
chamada? Você pode trabalhar muito, mas é realmente muito difícil (se não impossível) fazê-lo funcionar com segurança.Ataques em vários estágios
Agora, toda vez que você usa a entrada do usuário, você precisa pesar os benefícios de usá-la, contra os perigos. Proteja seu uso o máximo que puder.
Considere novamente o
eval
material capaz no exemplo de administrador. Eu te disse que estava tudo bem.Agora, considere que, na verdade, existe um lugar no seu aplicativo da Web em que você esqueceu de escapar do conteúdo do usuário (HTML, XSS). Essa é uma ofensa menor que a avaliação acessível pelo usuário. Mas, usando o conteúdo do usuário sem escape, um usuário pode assumir o navegador da Web de um administrador e adicionar um
eval
blob capaz na sessão de administradores, permitindo novamente o acesso total ao sistema.(O mesmo ataque de vários estágios pode ser feito com injeção de SQL em vez de XSS ou algumas gravações arbitrárias de arquivos substituindo o código executável em vez de usar
eval
)fonte
eval("hard coded string" + user_input_which_should_be_alphanumeric + "remainder")
. É possível verificar se a entrada é alfanumérica. Além disso, 'interrompe' é ortogonal a 'modifica / acessa o estado em que não deve tocar' e 'faz métodos calóricos que não deve chamar'.Para que esse recurso funcione, significa que preciso manter uma camada de reflexão que permita o acesso total ao estado interno de todo o programa.
Para linguagens interpretadas, posso simplesmente usar o estado do interpretador, o que é fácil, mas, em combinação com os compiladores JIT, ainda aumenta significativamente a complexidade.
Sem isso
eval
, o compilador JIT geralmente pode provar que os dados locais de um encadeamento não são acessados a partir de qualquer outro código, portanto, é perfeitamente aceitável reordenar acessos, omitir bloqueios e armazenar em cache os dados frequentemente usados por períodos mais longos. Quando outro encadeamento executa umaeval
instrução, pode ser necessário sincronizar o código compilado JIT em execução com isso, de repente o código gerado pelo JIT precisa de um mecanismo de fallback que retorne à execução não otimizada dentro de um prazo razoável.Esse tipo de código tende a ter muitos bugs sutis e difíceis de reproduzir e, ao mesmo tempo, também limita a otimização no compilador JIT.
Para linguagens compiladas, a troca é ainda pior: a maioria das otimizações é proibida, e eu preciso manter informações extensas sobre símbolos e um intérprete, para que a flexibilidade adicional geralmente não valha a pena - geralmente é mais fácil definir uma interface para algumas funções internas. estruturas, por exemplo, fornecendo uma visão de script e acesso simultâneo ao controlador ao modelo do programa .
fonte
Rejeito a premissa que
eval
é considerada mais maligna que a aritmética do ponteiro ou mais perigosa do que o acesso direto à memória e ao sistema de arquivos. Não conheço nenhum desenvolvedor sensato que acredite nisso. Além disso, os idiomas que suportam o acesso direto à memória aritmética / direta do ponteiro normalmente não suportameval
e vice-versa, portanto, tenho certeza de que frequência essa comparação seria relevante.Mas
eval
pode ser uma vulnerabilidade mais conhecida , pelo simples motivo de ser suportada pelo JavaScript. O JavaScript é uma linguagem em área restrita sem acesso direto à memória ou ao sistema de arquivos, portanto, simplesmente não possui essas vulnerabilidades, exceto as deficiências na própria implementação da linguagem.Eval
é, portanto, um dos recursos mais perigosos da linguagem, pois abre a possibilidade de execução arbitrária de códigos. Acredito que muito mais desenvolvedores desenvolvem em JavaScript do que em C / C ++, por issoeval
é simplesmente mais importante conhecer do que estouros de buffer para a maioria dos desenvolvedores.fonte
Nenhum programador sério consideraria Eval "mau". É simplesmente uma ferramenta de programação, como qualquer outra. O medo (se houver) dessa função não tem nada a ver com a cultura popular. É simplesmente um comando perigoso que geralmente é mal utilizado e pode apresentar sérias falhas de segurança e prejudicar o desempenho. Pela minha própria experiência, diria que é raro encontrar um problema de programação que não possa ser resolvido de maneira mais segura e eficiente por outros meios. Os programadores com maior probabilidade de usar o eval são aqueles com menos qualificação para fazê-lo com segurança.
Dito isto, existem idiomas em que o uso de eval é apropriado. Perl vem à mente. No entanto, eu pessoalmente acho que isso raramente é necessário em outras linguagens mais modernas, que oferecem suporte nativo ao tratamento estruturado de exceções.
fonte
Acho que você o abordou muito bem na parte seguinte da sua pergunta (grifo meu):
Para os 95% que podem usá-lo corretamente, tudo está bem; mas sempre haverá pessoas que não o usarão corretamente. Alguns deles se resumem a inexperiência e falta de habilidade; o restante será malicioso.
Sempre haverá pessoas que querem ultrapassar fronteiras e encontrar falhas de segurança - algumas para o bem, outras para o mal.
Quanto ao aspecto histórico, as
eval
funções de tipo permitem essencialmente a execução de código arbitrário que já havia sido explorado no popular CMS da web, Joomla! . Com o Joomla! acionando mais de 2,5 milhões de sites em todo o mundo , há muitos danos potenciais não apenas aos visitantes desses sites, mas também à infraestrutura em que está hospedado e também à reputação dos sites / empresas que foram explorados.O Joomla! exemplo pode ser simples, mas foi gravado.
fonte
Existem algumas boas razões para desencorajar o uso de
eval
(embora algumas sejam específicas para determinados idiomas).eval
é freqüentemente surpreendente (ou seja, você acha queeval(something here)
deve fazer uma coisa, mas faz outra, possivelmente desencadeando exceções)Pessoalmente, eu não chegaria ao ponto de dizer que isso é mau, mas sempre desafiarei o uso de
eval
uma revisão de código, com palavras na linha de "você considerou algum código aqui ?" (se eu tiver tempo para fazer pelo menos uma substituição provisória) ou "você tem certeza de que uma avaliação é realmente a melhor solução aqui?" (se não tiver).fonte
Na Sociedade da mente de Minsky , capítulo 6.4, ele diz
Quando um programa (B) escreve outro programa (A), ele está funcionando como um metaprograma. O assunto de B é o programa A.
Existe um programa C. Essa é a sua cabeça que escreve o programa B.
O perigo é que pode haver alguém (C ') com más intenções, que pode interferir nesse processo, para que você obtenha coisas como injeção de SQL.
O desafio é tornar o software mais inteligente sem também torná-lo mais perigoso.
fonte
Você tem muitas boas respostas aqui e o principal motivo é claramente que a execução arbitrária do código é ruim mmmkay, mas vou adicionar outro fator que outros apenas abordaram:
É realmente difícil solucionar problemas de código que está sendo avaliado em uma sequência de texto. Suas ferramentas de depuração normais são praticamente diretamente da janela e você é reduzido a traçados ou ecos da velha escola. Obviamente, isso não importa tanto se você tem um fragmento em uma linha que deseja,
eval
mas como programador experiente, provavelmente encontrará uma solução melhor para isso, enquanto a geração de código em larga escala pode estar em algum lugar em que uma função de avaliação torna-se um aliado potencialmente útil.fonte