¿Cuándo está eval evil en php?

84

En todos los años que he estado desarrollando en php, siempre he escuchado que usar eval()es malo.

Teniendo en cuenta el siguiente código, ¿no tendría sentido utilizar la segunda (y más elegante) opción? Si no es así, ¿por qué?

// $type is the result of an SQL statement
// e.g. SHOW COLUMNS FROM a_table LIKE 'a_column';
// hence you can be pretty sure about the consistency
// of your string
$type = "enum('a','b','c')";

// possibility one
$type_1 = preg_replace('#^enum\s*\(\s*\'|\'\s*\)\s*$#', '', $type);
$result = preg_split('#\'\s*,\s*\'#', $type_1);

// possibility two
eval('$result = '.preg_replace('#^enum#','array', $type).';');
Pierre Spring
fuente
2
eval SIEMPRE es malvado, siempre hay una mejor manera de escribir código, especialmente desde que PHP introdujo funciones anónimas. En este caso, usaría$result = array(); preg_replace_callback('#^enum\s*\(\s*\'|\'\s*\)\s*$#', function($m) use($result) { $result[] = $m[1]; }, $type);
Geoffrey
Bueno, honestamente, creo que el principal problema con php no es el lenguaje en sí, sino la gente que lo usa. Las tres respuestas correctas a esta pregunta (thomasrutter, braincracking y mía) obtuvieron votos negativos sin que nadie tuviera un punto en contra. Por otro lado, una respuesta afirma que "A veces eval () es la única / la solución correcta" sin ejemplo o explicación y obtiene la máxima votación ...
Francois Bourgeois

Respuestas:

134

Sería cauteloso al llamar a eval () pura maldad. La evaluación dinámica es una herramienta poderosa y, a veces, puede salvar la vida. Con eval () se pueden solucionar las deficiencias de PHP (ver más abajo).

Los principales problemas con eval () son:

  • Entrada potencialmente insegura. Pasar un parámetro que no es de confianza es una forma de fallar. A menudo, no es una tarea trivial asegurarse de que un parámetro (o parte de él) sea completamente confiable.
  • Truco.Usar eval () hace que el código sea inteligente y, por lo tanto, más difícil de seguir. Para citar a Brian Kernighan, " Depurar es dos veces más difícil que escribir el código en primer lugar. Por lo tanto, si escribe el código de la manera más inteligente posible, por definición, no es lo suficientemente inteligente para depurarlo ".

El principal problema con el uso real de eval () es solo uno:

  • Desarrolladores sin experiencia que lo usan sin suficiente consideración.

Como regla general, tiendo a seguir esto:

  1. A veces eval () es la única / la solución correcta.
  2. En la mayoría de los casos, uno debería intentar otra cosa.
  3. Si no está seguro, vaya a 2.
  4. De lo contrario, tenga mucho, mucho cuidado.
Michał Rudnicki
fuente
4
Esto también se podría refactorizar para evitar eval (), especialmente si $ className está en la lista blanca, que debe ser para entretener el uso de eval (). Sin embargo, la buena noticia es que, a partir de 5.3, $ foo :: bar () es válido.
rojoca
@rojoca: ¿Puede darnos un ejemplo de cómo hacerlo sin eval () en PHP 5.2, por favor?
Michał Rudnicki
30
No estoy seguro si cuenta como eval o no, pero $ result = call_user_func (array ('Foo', 'bar')); Funciona de maravilla.
Ionuț G. Stan
3
Buen punto sobre la astucia: en su ejemplo (simple), una variable aparece "de la nada". Si el código se vuelve un poco más complejo, buena suerte para la siguiente persona que mire el código e intente perseguir esa variable (he estado allí, hecho eso, todo lo que tengo fue un dolor de cabeza y esta pésima camiseta).
Piskvor salió del edificio el
La entrada insegura es evitable
thepowerlies
40

eval es malo cuando existe la más mínima posibilidad de que la entrada del usuario esté incluida en la cadena evaluada. Cuando realiza una evaluación sin contenido que proviene de un usuario, debe estar seguro.

Sin embargo, debería pensar al menos dos veces antes de usar eval, parece engañosamente simple, pero teniendo en cuenta el manejo de errores (ver el comentario de VBAssassins), la capacidad de depuración, etc., ya no es tan simple.

Entonces, como regla general: Olvídate de eso. ¡Cuando eval es la respuesta, probablemente estás haciendo la pregunta incorrecta! ;-)

Patrick Cornelissen
fuente
6
A veces eval ES la respuesta. Trabajamos en aplicaciones de juegos en línea y es muy difícil evitar las evaluaciones allí debido a las relaciones muy complejas entre las entidades ... pero en mi humilde opinión, en el 90% de los casos, eval no es la respuesta.
Jet
19
Tendría mucha curiosidad por ver una situación en la que SOLO eval es la respuesta.
Ionuț G. Stan
2
@Ionut G. Stan ¿Desencadenadores personalizados almacenados en la base de datos para objetos / entidades?
Kuroki Kaze
8
Realmente no creo que ninguno de esos sean usos justificados de eval (). En cada caso, hacer lo mismo sin usar eval () sigue siendo al menos posible. No es como si PHP estuviera lisiado sin eval (). Por supuesto, eval () es un atajo en casos como estos, pero aún hace que la ruta del código sea un poco más difícil de seguir y los problemas un poco más difíciles de depurar. Esa es mi opinión.
thomasrutter
4
@Christian: USTED debe escribir un ejemplo primero (use pastebin.com y pegue el enlace aquí), donde crea que evitar el uso de eval()es imposible, este es un mejor enfoque. Mucha gente dice que eval()es inevitable en algunos casos, pero no mencionan ningún ejemplo específico que podamos argumentar, de esta manera este debate no tiene sentido. Así que por favor, gente que dice que eval()es inevitable, ¡pruébenlo primero!
Sk8erPeter
18

eval () es igualmente malvado en todo momento.

"¿Cuándo eval () no es malo?" es la pregunta incorrecta en mi opinión, porque parece implicar que los inconvenientes de usar eval () desaparecen mágicamente en algunos contextos.

El uso de eval () es generalmente una mala idea porque disminuye la legibilidad del código, la capacidad de predecir la ruta del código (y las posibles implicaciones de seguridad de eso) antes del tiempo de ejecución y, por lo tanto, la capacidad de depurar el código. El uso de eval () también puede evitar que el código evaluado y el código que lo rodea sea optimizado por una caché de código de operación como Zend Opcache integrado en PHP 5.5 y superior, o por un compilador JIT como el de HHVM.

Además, no existe ninguna situación para la que sea absolutamente necesario utilizar eval (); PHP es un lenguaje de programación totalmente capaz sin él.

Si realmente los ve o no como males o si puede justificar personalmente el uso de eval () en algunos casos, depende de usted. Para algunos, los males son demasiado grandes como para justificarlos, y para otros, eval () es un atajo útil.

Sin embargo, si ve eval () como malvado, es malvado en todo momento. No pierde mágicamente su maldad según el contexto.

thomasrutter
fuente
1
Eres un gran programador.
Christian
1
"Además, no existe ninguna situación en la que sea absolutamente necesario usar eval ()". ¿Qué pasa si quiero ejecutar el código que he almacenado en una base de datos, según el ejemplo que se da en los documentos de PHP?
Yarin
4
A eso, diría que almacenar código PHP en la base de datos es igualmente malo e igualmente innecesario. Sea lo que sea lo que desee lograr, hay otras formas de hacerlo además de almacenar PHP en la base de datos o usar eval (). Hágalo si lo desea, y si es un atajo útil para usted, pero si trata a eval () como malvado, probablemente también tenga que tratar el almacenamiento de PHP en la base de datos como malvado.
thomasrutter
12
Agrega otro vector de ataque: una inyección SQL también podría ejecutar código PHP arbitrario en el servidor web. Requiere el uso de eval (). No se puede optimizar mediante cachés de códigos de bytes. Viola el principio de separar su código y sus datos. Puede hacer que su aplicación sea menos portátil: para actualizar / corregir PHP, también debe actualizar la base de datos.
thomasrutter
3
Yo diría que si algo solo se puede lograr mediante el uso de eval, sería bueno reconsiderar hacer ese algo. La creación dinámica de clases en tiempo de ejecución parece una mala idea. ¿Por qué no emplear algo de generación de código y generar el código que los define antes de tiempo?
thomasrutter
15

En este caso, eval es probablemente lo suficientemente seguro, siempre que nunca sea posible que un usuario cree columnas arbitrarias en una tabla.

Sin embargo, en realidad no es más elegante. Esto es básicamente un problema de análisis de texto, y abusar del analizador de PHP para manejarlo parece un poco complicado. Si desea abusar de las funciones del idioma, ¿por qué no abusar del analizador JSON? Al menos con el analizador JSON, no hay ninguna posibilidad de inyección de código.

$json = str_replace(array(
    'enum', '(', ')', "'"), array)
    '',     '[', ']', "'"), $type);
$result = json_decode($json);

Una expresión regular es probablemente la forma más obvia. Puede usar una sola expresión regular para extraer todos los valores de esta cadena:

$extract_regex = '/
    (?<=,|enum\()   # Match strings that follow either a comma, or the string "enum("...
    \'      # ...then the opening quote mark...
    (.*?)       # ...and capture anything...
    \'      # ...up to the closing quote mark...
    /x';
preg_match_all($extract_regex, $type, $matches);
$result = $matches[1];
BlackAura
fuente
1
aunque esto no respondió la pregunta per se, esta es una muy buena respuesta a la pregunta: ¿cuál sería la mejor manera de analizar una enumeración ()… gracias;)
Pierre Spring
13

eval() es lento, pero yo no lo llamaría malvado.

Es el mal uso que hacemos de él lo que puede llevar a la inyección de código y ser malvado.

Un simple ejemplo:

$_GET = 'echo 5 + 5 * 2;';
eval($_GET); // 15

Un ejemplo dañino:

$_GET = 'system("reboot");';
eval($_GET); // oops

Le aconsejaría que no lo use, eval()pero si lo hace, asegúrese de validar / incluir en la lista blanca todas las entradas.

Alix Axel
fuente
12

Cuando utiliza datos externos (como la entrada del usuario) dentro de eval.

En su ejemplo anterior, esto no es un problema.

GreenieMeanie
fuente
7

voy a robar descaradamente el contenido aquí:

  1. Eval, por su naturaleza, siempre será un problema de seguridad.

  2. Además de las preocupaciones de seguridad, eval también tiene el problema de ser increíblemente lento. En mis pruebas en PHP 4.3.10 es 10 veces más lento que el código normal y 28 veces más lento en PHP 5.1 beta1.

blog.joshuaeichorn.com: usando-eval-en-php

stefs
fuente
5

eval()es siempre malvado.

  • por razones de seguridad
  • por razones de rendimiento
  • por razones de legibilidad / reutilización
  • por razones de IDE / herramienta
  • por motivos de depuración
  • Siempre hay una mejor manera
Francois burgués
fuente
@bracketworks: Pero estás equivocado: todos estos problemas inherentes no desaparecen mágicamente en algunas situaciones. ¿Por qué deberían hacerlo? Tomemos como ejemplo el rendimiento: ¿De verdad crees que el intérprete ejecutará la función extremadamente lenta eval () con mayor velocidad solo porque la usaste de alguna manera mística "no malvada"? ¿O esa depuración paso a paso funcionará dentro de su cláusula de evaluación en algún día de suerte?
Francois Bourgeois
3
Francamente, creo que la respuesta de @ MichałRudnicki lo dice mejor. No me malinterpretes, cada vez que me hago (a mí mismo ) una pregunta, y la respuesta es eval(), supongo que he hecho la pregunta mal; rara vez es la respuesta "correcta", pero afirmar que siempre es malvado es simplemente incorrecto.
Dan Lugg
si quiero almacenar el código en la base de datos? ¿cómo puedo ejecutarlo?
Konstantin XFlash Stratigenas
@KonstantinXFlashStratigenas Debería preguntarse por qué está almacenando código ejecutable en una base de datos. Una base de datos es para datos resultantes de su código, no su código. Como mínimo, está aumentando los vectores de ataque.
Jack B
4

También prestaría algo de consideración a las personas que mantienen su código.

eval () no es fácil de mirar y saber lo que se supone que debe suceder, tu ejemplo no es tan malo, pero en otros lugares puede ser una verdadera pesadilla.


fuente
4

Personalmente, creo que el código sigue siendo bastante malo porque no estás comentando lo que está haciendo. Tampoco está probando la validez de sus entradas, lo que lo hace muy frágil.

También creo que, dado que el 95% (o más) de los usos de eval son activamente peligrosos, el pequeño ahorro de tiempo potencial que podría proporcionar en otros casos no vale la pena permitirse la mala práctica de usarlo. Además, luego tendrás que explicar a tus secuaces por qué tu uso de eval es bueno y el de ellos malo.

Y, por supuesto, su PHP termina pareciéndose a Perl;)

Hay dos problemas clave con eval (), (como un escenario de "ataque de inyección"):

1) Puede causar daño 2) Simplemente puede fallar

y uno que es más social que técnico:

3) Tentará a las personas a usarlo de manera inapropiada como atajo en otros lugares

En el primer caso, corre el riesgo (obviamente, no cuando está evaluando una cadena conocida) de la ejecución de código arbitrario. Sin embargo, es posible que sus entradas no sean tan conocidas o tan fijas como cree.

Es más probable (en este caso) que se bloquee y su cadena termine con un mensaje de error innecesariamente oscuro. En mi humilde opinión, todo el código debería fallar lo más claramente posible, en caso contrario, debería generar una excepción (como la forma de error más manejable).

Sugeriría que, en este ejemplo, esté codificando por coincidencia en lugar de codificar el comportamiento. Sí, la declaración de enumeración de SQL (y ¿está seguro de la enumeración de ese campo? - ¿llamó al campo correcto de la tabla correcta de la versión correcta de la base de datos? ¿En realidad respondió?) Parece tener la sintaxis de declaración de matriz en PHP, pero sugiero que lo que realmente quiere hacer no es encontrar el camino más corto desde la entrada hasta la salida, sino abordar la tarea especificada:

  • Identifica que tienes una enumeración
  • Extrae la lista interna
  • Desempaquetar los valores de la lista

Que es aproximadamente lo que hace su opción uno, pero envolvería algunos if y comentarios para mayor claridad y seguridad (por ejemplo, si la primera coincidencia no coincide, arroje una excepción o establezca un resultado nulo).

Todavía hay algunos problemas posibles con las comillas o comillas de escape, y probablemente debería descomprimir los datos y luego eliminarlos, pero al menos trata los datos como datos, en lugar de como código.

Con preg_version, es probable que su peor resultado sea $ result = null, con la versión eval se desconoce el peor, pero al menos un bloqueo.

Parsingphase
fuente
3

eval evalúa una cadena como código, el problema es que si la cadena está "contaminada" de alguna manera, podría exponer enormes amenazas de seguridad. Normalmente, el problema es en un caso en el que la entrada del usuario se evalúa en la cadena; en muchos casos, el usuario podría ingresar código (php o ssi, por ejemplo) que luego se ejecuta dentro de eval, se ejecutaría con los mismos permisos que su script php y podría ser utilizado para obtener información / acceso a su servidor. Puede ser bastante complicado asegurarse de que la entrada del usuario se elimine correctamente antes de entregársela a eval. Hay otros problemas ... algunos de los cuales son discutibles

Toby
fuente
3

PHP aconseja que escriba su código de tal manera que se pueda ejecutar a través de call_user_func en lugar de realizar evaluaciones explícitas.

Nolte
fuente
2

Otra razón evales que no se puede almacenar en caché mediante cachés de código de bytes de PHP como eAccelertor o ACP.

El hipopótamo
fuente
2

Es una mala programación lo que hace que eval () sea malo, no la función. Lo uso a veces, ya que no puedo evitarlo en la programación dinámica en varios sitios. No puedo hacer que PHP sea analizado en un sitio, ya que no recibiré las cosas que quiero. ¡Solo recibiría un resultado! Estoy feliz de que exista una función como eval (), ya que hace mi vida mucho más fácil. ¿Entrada del usuario? Los hackers sólo enganchan a los programadores malos. No me preocupo por eso.

Igor M. - PortalPress.org
fuente
2

Es una mala programación lo que hace que eval () sea malo, no la función. Lo uso a veces, ya que no puedo evitarlo en la programación dinámica en varios sitios. No puedo hacer que PHP sea analizado en un sitio, ya que no recibiré las cosas que quiero. ¡Solo recibiría un resultado! Estoy feliz de que exista una función como eval (), ya que hace mi vida mucho más fácil. ¿Entrada del usuario? Los hackers sólo enganchan a los programadores malos. No me preocupo por eso.

Predigo que pronto tendrás serios problemas ...

Honestamente, no hay absolutamente ningún buen uso para una función exorbitante como eval, en un lenguaje interpretado como PHP. Nunca he visto a eval realizar funciones de programa que no se podrían haber ejecutado usando otras formas más seguras ...

Eval es la raíz de todos los males, estoy totalmente de acuerdo, para todas las personas que piensan que probar la entrada del usuario ayudará. Piénselo dos veces, la entrada del usuario puede tener muchas formas diferentes y, mientras hablamos, los piratas informáticos están explotando esa función que no le importaba lo suficiente. En mi opinión, simplemente evite eval por completo.

He visto ejemplos elaborados para abusar de la función eval que sobrepasó mi propia creatividad. Desde una posición de seguridad, evítelo a toda costa, e incluso iría tan lejos como para exigir que sea al menos una opción en la configuración de PHP, en lugar de un 'dado'.

Braincracking
fuente
El razonamiento de "no importa qué tan cuidadosamente intente asegurar su código con respecto a la característica X, los piratas informáticos inteligentes siempre están un paso por delante ..." podría aplicarse a cualquier otra técnica, no solo a X == eval. Y por lo tanto, para cualquier característica, X: X es la raíz de todo eval ... er ... evil, así que mejor renuncie a la programación por completo (tirando así de la alfombra debajo de esos tontos hackers, aún burlándolos eventualmente).
Sz.
"No hay absolutamente ningún buen uso para una función exorbitante como eval" -> cualquiera que pueda usar palabras como 'absolutamente', sea nuevo en programación o mal programador.
unity100
2

Aquí hay una solución para ejecutar código PHP extraído de una base de datos sin usar eval. Permite todas las funciones y excepciones del alcance:

$rowId=1;  //database row id
$code="echo 'hello'; echo '\nThis is a test\n'; echo date(\"Y-m-d\");"; //php code pulled from database

$func="func{$rowId}";

file_put_contents('/tmp/tempFunction.php',"<?php\nfunction $func() {\n global \$rowId;\n$code\n}\n".chr(63).">");

include '/tmp/tempFunction.php';
call_user_func($func);
unlink ('/tmp/tempFunction.php');

Básicamente, crea una función única con el código incluido en un archivo de texto, incluye el archivo, llama a la función y luego elimina el archivo cuando termina con él. Estoy usando esto para realizar ingestiones / sincronizaciones diarias de bases de datos donde cada paso requiere un código único para procesar. Esto ha resuelto todos los problemas a los que me enfrentaba.

Jason
fuente
Esto parece una respuesta a una respuesta; publíquelo como tal cuando pueda.
rfornal
1

Solía ​​usar eval () mucho, pero encontré que en la mayoría de los casos no es necesario usar eval para hacer trucos. Bueno, tienes call_user_func () y call_user_func_array () en PHP. Es lo suficientemente bueno para llamar estática y dinámicamente a cualquier método.

Para realizar una llamada estática, construya su devolución de llamada como una matriz ('class_name', 'method_name'), o incluso como una cadena simple como 'class_name :: method_name'. Para realizar una llamada dinámica, use la devolución de llamada de estilo array ($ object, 'method').

El único uso sensato de eval () es escribir un compilador personalizado. Hice uno, pero eval sigue siendo malo, porque es muy difícil de depurar. Lo peor es que el error fatal en el código evaluado bloquea el código que lo llamó. Usé la extensión Parsekit PECL para verificar la sintaxis al menos, pero aún así no me alegro: intente hacer referencia a clases desconocidas y fallas de aplicaciones completas.

Harry
fuente
0

Aparte de las preocupaciones de seguridad, eval () no se puede compilar, optimizar o almacenar en caché el código de operación, por lo que siempre será más lento, mucho más lento , que el código php normal. Por lo tanto, no es eficaz usar eval, aunque eso no lo convierte en malo. ( gotoes malvado, evales solo una mala práctica / código maloliente / feo)

Aron Cederholm
fuente
evalobtiene una calificación de maldad más baja que goto? ¿Es el día opuesto?
JLRishe
Es solo que le guardo rencor a los desarrolladores de php por implementar realmente gotoen 5.3.
Aron Cederholm
1
No, a los gotofobianos: están las herramientas, y están los artesanos, que las usan (o no). Es que nunca las herramientas que hacen un aspecto profesional como un idiota, cuando cometer errores, sino más bien la imposibilidad de utilizar las herramientas adecuadas (correctamente). Pero siempre son las herramientas las que tienen la culpa ...
Sz.
0

La mayoría de la gente señalará el hecho de que puede ser peligroso cuando se trata de la entrada del usuario (que es posible de manejar).

Para mí, la peor parte es que reduce la capacidad de mantenimiento de su código:

  • Difícil de depurar
  • Difícil de actualizar
  • Limita el uso de herramientas y ayudantes (como IDE)
los poderosos
fuente