¿Cuándo el eval () de JavaScript no es malo?


263

Estoy escribiendo un código JavaScript para analizar las funciones ingresadas por el usuario (para una funcionalidad similar a una hoja de cálculo). Después de analizar la fórmula, podría convertirla en JavaScript y ejecutarla eval()para obtener el resultado.

Sin embargo, siempre he evitado usarlo eval()si puedo evitarlo porque es malo (y, correcta o incorrectamente, siempre he pensado que es aún más malo en JavaScript, porque el usuario puede cambiar el código a evaluar )

Entonces, ¿cuándo está bien usarlo?


55
La mayoría de las bibliotecas JSON no utilizan, de hecho, eval bajo el capó, exactamente para protegerse contra los riesgos de seguridad.
Sean McMillan

11
@Sean: tanto JQuery como Prototype usan eval (JQuery lo usa a través de una nueva función)
plodder

55
@plodder: ¿de dónde sacas tu información? ¡jQuery ha utilizado el JSON.parse () nativo desde 1.4 (allá en 1/2010)! Véalo
ken

3
"Obviamente, uno tiene que usar eval () para analizar JSON" ; esto no es cierto, por el contrario, ¡ no se debe usar eval para analizar JSON! ¡Usa el script json2.js de Douglas Crockfords (creador de JSON) de json.org !
TMS

11
@Tomas la ironía de que json2.js usa eval para analizar JSON
tobyodavies

Respuestas:


262

Me gustaría tomar un momento para abordar la premisa de su pregunta: que eval () es " malvado ". La palabra " maldad ", como la usan las personas del lenguaje de programación, generalmente significa "peligroso", o más precisamente "capaz de causar mucho daño con un comando simple". Entonces, ¿cuándo está bien usar algo peligroso? Cuando sepa cuál es el peligro y cuando esté tomando las precauciones adecuadas.

Al punto, veamos los peligros en el uso de eval (). Probablemente hay muchos pequeños peligros ocultos como todo lo demás, pero los dos grandes riesgos, la razón por la cual eval () se considera malo, son el rendimiento y la inyección de código.

  • Rendimiento: eval () ejecuta el intérprete / compilador. Si su código está compilado, entonces este es un gran éxito, porque necesita llamar a un compilador posiblemente pesado en medio del tiempo de ejecución. Sin embargo, JavaScript sigue siendo principalmente un lenguaje interpretado, lo que significa que llamar a eval () no es un gran éxito de rendimiento en el caso general (pero vea mis comentarios específicos a continuación).
  • Inyección de código: eval () potencialmente ejecuta una cadena de código con privilegios elevados. Por ejemplo, un programa que se ejecuta como administrador / root nunca querría evaluar la entrada del usuario (), porque esa entrada podría ser "rm -rf / etc / important-file" o peor. Nuevamente, JavaScript en un navegador no tiene ese problema, porque el programa se está ejecutando en la cuenta del usuario de todos modos. JavaScript del lado del servidor podría tener ese problema.

En su caso específico. Por lo que entiendo, está generando las cadenas usted mismo, por lo que suponiendo que tenga cuidado de no permitir que se genere una cadena como "rm -rf algo importante", no hay riesgo de inyección de código (pero recuerde, es muy muy difícil de asegurar esto en el caso general). Además, si está ejecutando en el navegador, la inyección de código es un riesgo bastante menor, creo.

En cuanto al rendimiento, tendrá que compararlo con la facilidad de codificación. Es mi opinión que si está analizando la fórmula, también podría calcular el resultado durante el análisis en lugar de ejecutar otro analizador (el que está dentro de eval ()). Pero puede ser más fácil codificar usando eval (), y el impacto en el rendimiento probablemente pasará desapercibido. Parece que eval () en este caso no es más malvado que cualquier otra función que pueda ahorrarle algo de tiempo.


78
No está abordando el problema de código que utiliza eval siendo difícil de depurar
bobobobo

48
La inyección de código es un problema muy serio para javascript si le preocupan los datos de su usuario. El código inyectado se ejecutará (en el navegador) como si viniera de su sitio, permitiéndole hacer cualquier tipo de travesura que el usuario pueda hacer manualmente. Si permite que el código (de terceros) ingrese a su página, puede ordenar cosas en nombre de su cliente, o cambiar su gravatar, o lo que sea que puedan hacer a través de su sitio. Ten mucho cuidado. Dejar que los piratas informáticos sean dueños de sus clientes es tan malo como dejar que sean dueños de su servidor.
Sean McMillan

71
Si los datos provienen de su servidor y es algo que usted, el desarrollador ha generado, no hay daño al usar eval (). El verdadero daño es creer todo lo que lees. Ves a muchas personas que dicen que eval () es malo y no tienen idea de por qué, excepto que lo leyeron en alguna parte.
Vince Panuccio

42
@Sean McMillan: Quiero creerte, pero si alguien va a interceptar y cambiar javascript eval()desde tu servidor, también podría cambiar la fuente de la página en primer lugar, y también tomar el control de la información del usuario. . . No veo la diferencia
Walt W

20
Re "Inyección de código - ... De nuevo, JavaScript en un navegador no tiene ese problema" y "Además, si está ejecutando en el navegador, entonces la inyección de código es un riesgo bastante menor, creo". ¿Estás sugiriendo que la inyección de código en el navegador no es un problema? XSS ha estado en el top 3 vulns en la lista de los 10 mejores de OWASP durante varios años consecutivos.
Mike Samuel

72

eval()no es malo O, si es así, es malo de la misma manera que la reflexión, la E / S de archivos / redes, los subprocesos y el IPC son "malos" en otros idiomas.

Si, para su propósito , eval()es más rápido que la interpretación manual, o hace que su código sea más simple o más claro ... entonces debe usarlo. Si ninguno de los dos, entonces no deberías. Simple como eso.


55
Uno de estos propósitos podría ser generar código optimizado que sería demasiado largo o demasiado repetitivo para escribir a mano. El tipo de cosas que, en LISP, requerirían una macro.
wberry

55
Este es un consejo tan general que podría aplicarse literalmente a cualquier bloque de código que exista. Realmente no agrega nada a esta pregunta; en particular, no ayuda a nadie que venga a determinar si su uso particular es problemático o no.
jpmc26

2
Más rápido, más simple, más claro ... Esta respuesta no cubre las implicaciones de seguridad lo suficientemente bien.
Ruud Helderman

55

Cuando confías en la fuente.

En el caso de JSON, es más o menos difícil alterar la fuente, ya que proviene de un servidor web que controlas. Mientras el JSON en sí no contenga datos que haya cargado un usuario, no hay inconvenientes importantes para usar eval.

En todos los demás casos, haría todo lo posible para garantizar que los datos proporcionados por el usuario se ajusten a mis reglas antes de enviarlos a eval ().


13
Una cadena json siempre debe probarse con la gramática json antes de usarla en eval (). Por lo tanto, la cadena json "{foo: alert ('XSS')}" no pasaría ya que "alert ('XSS')" no es un valor adecuado.
Gumbo

3
Bueno, usa HTTPS, entonces. OTOH: man-in-the-middle no es el escenario de ataque típico para la aplicación web de variedades de jardín, mientras que, por ejemplo, lo es el cross-site-scripting.
Tomalak

77
evaltampoco analizará correctamente todas las cadenas JSON válidas. Por ejemplo, JSON.parse(' "\u2028" ') === "\u2028"pero eval(' "\u2028" ')plantea una excepción porque U + 2028 es una nueva línea en JavaScript, pero no es una nueva línea en lo que respecta a JSON.
Mike Samuel

1
@Justin: si el protocolo se ve comprometido, bueno, normalmente la carga de la página inicial se habría enviado a través de ese mismo protocolo, y luego es un punto discutible porque el cliente ya está tan comprometido como podría estarlo.
antinome

1
Bellamente dijo @Tomalak, ¡mencioné esto en mi respuesta ahora mismo! ¡Increíble!
NiCk Newman

25

Consigamos gente real:

  1. Todos los principales navegadores ahora tienen una consola incorporada que su posible pirata informático puede usar con abundancia para invocar cualquier función con algún valor, ¿por qué se molestarían en usar una declaración de evaluación, incluso si pudieran?

  2. Si toma 0.2 segundos compilar 2000 líneas de JavaScript, ¿cuál es mi degradación del rendimiento si evalúo cuatro líneas de JSON?

Incluso la explicación de Crockford para 'evaluar es malo' es débil.

eval es malvado, la función eval es la característica más mal utilizada de JavaScript. Evítalo

Como el propio Crockford podría decir: "Este tipo de afirmación tiende a generar neurosis irracional. No lo compre".

Comprender eval y saber cuándo podría ser útil es mucho más importante. Por ejemplo, eval es una herramienta sensata para evaluar las respuestas del servidor que fueron generadas por su software.

Por cierto: Prototype.js llama a eval directamente cinco veces (incluso en evalJSON () y evalResponse ()). jQuery lo usa en parseJSON (a través del constructor de funciones).


10
JQuery usa la función incorporada JSON.parse del navegador si está disponible (que es mucho más rápido y seguro), usando eval solo como mecanismo de reserva. La afirmación "eval is evil" es una guía razonablemente buena.
jjmontes

30
Re "Todos los principales navegadores ahora tienen una consola integrada ...". La inyección de código es un problema cuando un usuario puede ingresar el código que luego se ejecuta en el navegador de otro usuario. Las consolas del navegador no permiten por sí mismas que un usuario ejecute código en el navegador de otro usuario, por lo que son irrelevantes al decidir si vale la pena protegerse contra la inyección de código.
Mike Samuel

28
"Todos los principales navegadores ahora tienen una consola integrada ... ¿por qué se molestarían en usar una declaración eval?" - Estás lejos de la marca. Te sugiero que edites la respuesta. La capacidad de un usuario para inyectar código que puede ejecutarse en el navegador de otro es un problema importante. Y aquí es donde necesitas ser realmente real.
akkishore

55
@akkishore, agradeceré si se te ocurre un ejemplo de la vida real que respalde tus declaraciones más que expresadas.
Akash Kava

77
@AkashKava De lo que no te das cuenta es que si envío javascript en mi cuadro de comentarios, y que javascript llegue a la base de datos. Cuando otro usuario ve ese comentario (en el que puse javascript), eval tomará ese javascript cuando se procese y lo evaluará usando el intérprete, haciendo que mi javascript incrustado se ejecute en el navegador del otro usuario. Al hacer esto, puedo obtener todo tipo de información. Su nombre de usuario, su identificación de usuario en la base de datos, su dirección de correo electrónico, etc. Esta no es una respuesta difícil, si hubiera buscado en Google XSS, vería en unos 10 segundos por qué es un problema.
Kyle Richter

18

Tiendo a seguir el consejo de Crockford para eval(), y evitar por completo. Incluso las formas que parecen requerirlo no lo hacen. Por ejemplo, el le setTimeout()permite pasar una función en lugar de evaluar.

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

Incluso si se trata de una fuente confiable , no lo uso, porque el código devuelto por JSON podría estar confuso, lo que en el mejor de los casos podría hacer algo inestable, en el peor de los casos, exponer algo malo.


2
Creo que los errores en el formateador JSON en el lado del servidor son ciertamente un problema. ¿La respuesta del servidor depende de algún tipo de texto enviado por el usuario? Entonces tienes que estar atento a XSS.
swilliams

3
Si su servidor web no está autenticado a través de HTTPS, entonces podría sufrir algún tipo de ataque de hombre en el medio donde otro host intercepta la solicitud y envía sus propios datos.
Ben Combee

11
Si alguien puede realizar un ataque de hombre en el medio, puede inyectar fácilmente cualquier cosa en sus guiones.
el.pescado

10
No debe confiar en su código javascript en absoluto ... No debe confiar en nada que se ejecute en el lado del cliente ... Si alguien hace un ataque man-in-the-middle, ¿por qué meterse con sus objetos json? Él puede servirle una página web diferente y diferentes archivos js ...
Calmarius

55
Personalmente no me gusta el argumento "siempre hay otras formas de hacerlo". Por ejemplo, también podría decir que siempre hay formas de evitar la programación orientada a objetos. Eso no significa que no sea una gran opción. Si comprende Eval y sus peligros, puede ser una gran herramienta para usar en las situaciones correctas.
dallin

4

Vi a las personas abogar por no usar eval, porque es malo , pero vi que las mismas personas usan Function y setTimeout dinámicamente, por lo que usan eval bajo las capuchas : D

Por cierto, si su sandbox no está lo suficientemente seguro (por ejemplo, si está trabajando en un sitio que permite la inyección de código) eval es el último de sus problemas. La regla básica de seguridad es que todas las entradas son malas, pero en el caso de JavaScript, incluso el propio JavaScript podría ser malo, porque en JavaScript puedes sobrescribir cualquier función y no puedes estar seguro de que estás usando la real, así que, si un código malicioso comienza antes que usted, no puede confiar en ninguna función incorporada de JavaScript: D

Ahora el epílogo de esta publicación es:

Si REALMENTE lo necesita (el 80% del tiempo NO es necesario evaluar ) y está seguro de lo que está haciendo, simplemente use eval (o mejor Función;)), los cierres y la POO cubren el 80/90% del caso donde eval puede ser reemplazado usando otro tipo de lógica, el resto es código generado dinámicamente (por ejemplo, si está escribiendo un intérprete) y como ya dijo evaluando JSON (aquí puede usar la evaluación segura de Crockford;))


Y como señaló el propio Crockford , los navegadores web actuales tienen una función incorporada JSON.parse .
Ruud Helderman

4

Eval es complementario a la compilación que se usa para crear plantillas para el código. Por plantilla quiero decir que escribes un generador de plantillas simplificado que genera un código de plantilla útil que aumenta la velocidad de desarrollo.

He escrito un marco, donde los desarrolladores no usan EVAL, pero usan nuestro marco y, a su vez, ese marco tiene que usar EVAL para generar plantillas.

El rendimiento de EVAL puede aumentarse utilizando el siguiente método; en lugar de ejecutar el script, debe devolver una función.

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

Debería organizarse como

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

var a = f(3,5);

El almacenamiento en caché de f sin duda mejorará la velocidad.

También Chrome permite la depuración de tales funciones con mucha facilidad.

En cuanto a la seguridad, el uso de eval o not no hará ninguna diferencia,

  1. En primer lugar, el navegador invoca todo el script en una caja de arena.
  2. Cualquier código que sea malo en EVAL, es malo en el navegador mismo. El atacante o cualquier persona puede inyectar fácilmente un nodo de script en DOM y hacer cualquier cosa si puede evaluar cualquier cosa. No usar EVAL no hará ninguna diferencia.
  3. La mayor parte de la seguridad del lado del servidor es perjudicial. La mala validación de las cookies o la implementación deficiente de ACL en el servidor causa la mayoría de los ataques.
  4. Una vulnerabilidad reciente de Java, etc., estaba presente en el código nativo de Java. JavaScript se diseñó y se diseñó para ejecutarse en un entorno limitado, mientras que los applets se diseñaron para ejecutarse fuera de un entorno limitado con certificados, etc.
  5. Escribir código para imitar un navegador no es difícil. Todo lo que tiene que hacer es realizar una solicitud HTTP al servidor con su cadena de agente de usuario favorita. Todas las herramientas de prueba se burlan de los navegadores de todos modos; Si un atacante quiere hacerte daño, EVAL es su último recurso. Tienen muchas otras formas de lidiar con la seguridad del lado del servidor.
  6. El navegador DOM no tiene acceso a los archivos y no tiene un nombre de usuario. De hecho, nada en la máquina a la que eval pueda dar acceso.

Si la seguridad del lado del servidor es lo suficientemente sólida como para que cualquiera pueda atacar desde cualquier lugar, no debe preocuparse por EVAL. Como mencioné, si EVAL no existiera, los atacantes tienen muchas herramientas para hackear su servidor independientemente de la capacidad EVAL de su navegador.

Eval solo es bueno para generar algunas plantillas para hacer un procesamiento de cadenas complejo basado en algo que no se usa de antemano. Por ejemplo, preferiré

"FirstName + ' ' + LastName"

Opuesto a

"LastName + ' ' + FirstName"

Como mi nombre para mostrar, que puede provenir de una base de datos y que no está codificado.


Puede usar la función en lugar de eval - function (first, last) { return last + ' ' + first }.
Konrad Borowski

Los nombres de las columnas provienen de la base de datos.
Akash Kava

3
La amenaza de evales en su mayoría otros usuarios . Supongamos que tiene una página de configuración y le permite establecer cómo se ve su nombre a los demás. Digamos también que no estaba pensando muy claramente cuando lo escribió, por lo que su cuadro de selección tiene opciones como <option value="LastName + ' ' + FirstName">Last First</option>. Abro mis herramientas de desarrollo, cambio la valueopción alert('PWNED!'), selecciono la opción modificada y envío el formulario. Ahora, cada vez que otra persona puede ver mi nombre para mostrar, ese código se ejecuta.
cHao

@ cHao, del que está hablando es un ejemplo de seguridad del lado del servidor deficiente, el servidor nunca debe aceptar datos que puedan ejecutarse como código en el navegador de cualquier persona. Una vez más, no ha podido entender el concepto de seguridad del lado del servidor deficiente.
Akash Kava

1
Puede quejarse de la seguridad del lado del servidor si lo desea, pero el objetivo evales ejecutar código que no sea parte del script que escribió. Si no necesita el poder para hacerlo (y casi nunca lo hace), evitar evalayuda a evitar toda una categoría de problemas. Eso es bueno si su código del lado del servidor no es perfecto.
cHao

4

Al depurar en Chrome (v28.0.1500.72), descubrí que las variables no están vinculadas a cierres si no se usan en una función anidada que produce el cierre. Supongo que es una optimización del motor de JavaScript.

PERO : cuando eval()se usa dentro de una función que causa un cierre, TODAS las variables de las funciones externas están vinculadas al cierre, incluso si no se usan en absoluto. Si alguien tiene tiempo para probar si eso puede producir pérdidas de memoria, déjeme un comentario a continuación.

Aquí está mi código de prueba:

(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();
})();

Lo que me gusta señalar aquí es que eval () no necesariamente debe referirse a la eval()función nativa . Todo depende del nombre de la función . Entonces, al llamar al nativo eval()con un nombre de alias (digamos var noval = eval;y luego en una función interna noval(expression);), entonces la evaluación de expressionpuede fallar cuando se refiere a variables que deberían ser parte del cierre, pero en realidad no lo es.



3

Línea de fondo

Si creó o desinfectó el código eval, nunca es malo .

Ligeramente más detallado

evales malo si se ejecuta en el servidor utilizando entradas enviadas por un cliente que no fue creado por el desarrollador o que no fue desinfectado por el desarrollador .

evalNo es malo si se ejecuta en el cliente, incluso si se utiliza una entrada no higiénica creada por el cliente .

Obviamente , siempre debe desinfectar la entrada, para tener cierto control sobre lo que consume su código.

Razonamiento

El cliente puede ejecutar cualquier código arbitrario que desee, incluso si el desarrollador no lo codificó; Esto es cierto no solo para lo que se evade, sino para la llamada a evalsí mismo .


2

La única instancia en la que debería usar eval () es cuando necesita ejecutar JS dinámico sobre la marcha. Estoy hablando de JS que descargas de forma asíncrona desde el servidor ...

... Y 9 veces de 10 fácilmente podría evitar hacerlo refactorizando.


Hoy en día, hay otras (y mejores) formas de cargar JavaScript de forma asíncrona desde el servidor: w3bits.com/async-javascript
Ruud Helderman

1

evalRaramente es la elección correcta. Si bien puede haber numerosos casos en los que puede lograr lo que necesita lograr concatenando un script y ejecutándolo sobre la marcha, por lo general tiene técnicas mucho más potentes y fáciles de mantener a su disposición: notación de matriz asociativa ( obj["prop"]es lo mismo que obj.prop) , cierres, técnicas orientadas a objetos, técnicas funcionales: utilícelas en su lugar.


1

En lo que respecta al script del cliente, creo que el tema de la seguridad es un punto discutible. Todo lo cargado en el navegador está sujeto a manipulación y debe tratarse como tal. Existe un riesgo cero al usar una declaración eval () cuando hay formas mucho más fáciles de ejecutar código JavaScript y / o manipular objetos en el DOM, como la barra de URL en su navegador.

javascript:alert("hello");

Si alguien quiere manipular su DOM, digo alejarse. La seguridad para evitar cualquier tipo de ataque siempre debe ser responsabilidad de la aplicación del servidor, punto.

Desde un punto de vista pragmático, no es beneficioso usar un eval () en una situación en la que las cosas se pueden hacer de otra manera. Sin embargo, hay casos específicos en los que DEBE utilizarse una evaluación. Cuando sea así, definitivamente se puede hacer sin ningún riesgo de volar la 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>

66
Re "No hay ningún riesgo al usar una instrucción eval () cuando hay formas mucho más fáciles de ejecutar JavaScript y / o manipular objetos en el DOM". La inyección de código es un problema cuando un usuario puede ingresar el código que luego se ejecuta en el navegador de otro usuario. Las consolas del navegador no permiten por sí mismas que un usuario ejecute código en el navegador de otro usuario, por lo que son irrelevantes al decidir si vale la pena protegerse contra la inyección de código.
Mike Samuel

¿No es <head></head>obligatorio, incluso si está vacío?
Peter Mortensen el

2
Esta respuesta ignora por completo los riesgos de XSS .
Ruud Helderman

1

En el lado del servidor, eval es útil cuando se trata de scripts externos como sql o influxdb o mongo. Donde se puede realizar una validación personalizada en tiempo de ejecución sin volver a implementar sus servicios.

Por ejemplo, un servicio de logros con los siguientes metadatos

{
  "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 luego permiten,

  • Inyección directa de objetos / valores a través de una cadena literal en un json, útil para crear plantillas de textos

  • Se puede usar como un comparador, digamos que hacemos reglas sobre cómo validar misiones o eventos en CMS

Con de esto:

  • Puede haber errores en el código y romper cosas en el servicio, si no se prueba completamente.

  • Si un hacker puede escribir un script en su sistema, entonces está bastante jodido.

  • Una forma de validar su script es mantener el hash de sus scripts en un lugar seguro, para que pueda verificarlos antes de ejecutarlos.


Agradable. Cuando hice la pregunta, ni siquiera había estado pensando en JS del lado del servidor.
Richard Turner

1

Creo que cualquier caso de evaluación justificada sería raro. Es más probable que lo use pensando que está justificado que cuando lo usa realmente .

Los problemas de seguridad son los más conocidos. Pero también tenga en cuenta que JavaScript usa la compilación JIT y esto funciona muy mal con eval. Eval es algo así como una caja negra para el compilador, y JavaScript necesita poder predecir el código con anticipación (hasta cierto punto) para aplicar de forma segura y correcta las optimizaciones de rendimiento y el alcance. En algunos casos, el impacto en el rendimiento puede incluso afectar a otro código fuera de eval.

Si quieres saber más: https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20%26%20closures/ch2.md#eval


0

Está bien usarlo si tiene control completo sobre el código que se pasa a la evalfunción.


2
Si tienes control total sobre lo que estás pasando eval, entonces la gran pregunta es, ¿cuándo tiene sentido que sea una cadena en lugar de un JS real?
cHao

@cHao Por ejemplo, si tiene una aplicación de juego grande (5-10MB Javascript), es mejor construir primero un precargador AJAX de carga rápida simple (1kb), que carga el script principal grande, mientras muestra una carga- Bar o algo similar. Después de la descarga, puede usar "eval (fuente)" o mejor "nueva Función (fuente)" para ejecutar el Juego-Aplicación-Script cargado. De esa forma, el usuario puede ver visualmente que la aplicación necesita tiempo para descargarse hasta que el juego pueda comenzar. Sin eso, el usuario tiene que esperar a que se cargue toda la Aplicación sin ningún comentario visual.
SammieFox

@SammieFox Hay otras (y mejores) formas de hacerlo, sobre todo <script async="true" src="...">. Ver también: w3bits.com/async-javascript
Ruud Helderman

La respuesta es un consejo peligroso; demasiados desarrolladores tienen una falsa sensación de estar en control. El consejo tiene sentido para el software que ya no se mantiene activamente. Pero dicho software debe considerarse muerto.
Ruud Helderman

0

Solo durante las pruebas, si es posible. También tenga en cuenta que eval () es mucho más lento que otros evaluadores JSON especializados, etc.


0

No hay ninguna razón para no usar eval () siempre que pueda estar seguro de que la fuente del código proviene de usted o del usuario real. Aunque puede manipular lo que se envía a la función eval (), eso no es un problema de seguridad, porque puede manipular el código fuente del sitio web y, por lo tanto, podría cambiar el código JavaScript en sí.

Entonces ... ¿cuándo no usar eval ()? Eval () solo no debe usarse cuando existe la posibilidad de que un tercero pueda cambiarlo. Como interceptar la conexión entre el cliente y su servidor (pero si eso es un problema, use HTTPS). No debe evaluar () para analizar el código escrito por otros como en un foro.


Re "No hay ninguna razón para no usar eval () siempre que pueda estar seguro de que la fuente del código proviene de usted o del usuario real". Esto supone que hay un solo usuario. Esa premisa no se establece en el OP. Cuando hay varios usuarios, el descuido evalde una cadena compuesta del contenido de un usuario puede permitir que ese usuario ejecute código en el navegador del otro usuario.
Mike Samuel

@MikeSamuel, eval puede ejecutar código en el navegador de otro usuario, no he escuchado esto, ¿has intentado esto? Esto nunca sucedió en la historia de la navegación, ¿puede mostrarnos un ejemplo?
Akash Kava

@AkashKava, una cadena puede originarse con un agente de usuario, almacenarse en una base de datos y luego servirse a otro navegador que evalsea. Pasa todo el tiempo.
Mike Samuel

Base de datos @MikeSamuel? ¿dónde? ¿Quién sirve cadena incorrecta? ¿No es culpa de la base de datos del lado del servidor? en primer lugar, no se debe culpar a EVAL por el código lateral del servidor mal escrito. Use jsfiddle y muestre al mundo un ejemplo del mundo real donde puede causar daño.
Akash Kava

2
@AkashKava, no entiendo tu pregunta. No estamos hablando de una aplicación específica, sino de razones para no usarla eval. ¿Cómo es útil culpar al servidor? Si alguien debe ser culpado, debería ser el atacante. Independientemente de la culpa, un cliente que no es vulnerable a XSS a pesar de los errores en el servidor es mejor que un cliente que es vulnerable, todo lo demás es igual.
Mike Samuel

0

Si es realmente necesario, eval no es malo. Pero el 99.9% de los usos de eval con los que me tropiezo no son necesarios (sin incluir cosas de setTimeout).

Para mí, el mal no es un problema de rendimiento o incluso de seguridad (bueno, indirectamente son ambos). Todos esos usos innecesarios de eval se suman a un infierno de mantenimiento. Las herramientas de refactorización se desechan. Buscar código es difícil. Los efectos imprevistos de esas evaluaciones son legión.


55
eval no es necesario para setTimeout. Puede usar una referencia de función allí también.
Matthew Crumley

0

¿Cuándo el eval () de JavaScript no es malo?

Siempre trato de desalentar el uso de eval . Casi siempre, hay disponible una solución más limpia y fácil de mantener. Eval no es necesario incluso para el análisis JSON . Eval se suma al infierno de mantenimiento . No sin razón, es mal visto por maestros como Douglas Crockford.

Pero encontré un ejemplo donde debería usarse:

Cuando necesitas pasar la expresión.

Por ejemplo, tengo una función que construye un google.maps.ImageMapTypeobjeto general para mí, pero necesito decirle la receta, cómo debe construir la URL del mosaico a partir de los parámetros zoomy 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);
        },
        ....
    });
}

3
Parece que podría ser refactorizado para que eval () no sea necesario: tileURLexpr es solo una plantilla, por lo que un uso juicioso de replace () haría el trabajo. Aún así, me recuerda un ejemplo que tenía en mente cuando presenté la pregunta, que tenía que ver con permitir que un usuario especifique una fórmula matemática para evaluar, similar a la funcionalidad de la hoja de cálculo. ¡Por supuesto que no lo mencioné en ese momento porque no quería influir en las respuestas!
Richard Turner

8
tileURL: function (zoom, coord) { return 'http://tile.openstreetmap.org/' + b + '/' + a.x + '/' + a.y + '.png'; },
Casey Chu

0

Mi ejemplo de uso eval: import .

Cómo se hace generalmente.

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

Pero con la ayuda evaly una pequeña función auxiliar, se ve mucho mejor:

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

importable podría verse así (esta versión no admite la importación de miembros 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;
}

2
1 de la idea, pero tiene un error aquí: .replace(/name/g, name).replace('path', path). Si namecontiene la cadena, "path"entonces podría obtener sorpresas.
wberry

1
Declarar una variable para cada propiedad de componentses un posible código de olor; refactorizar su código podría eliminar el 'problema' por completo. Su solución actual es solo azúcar sintáctico. Si insiste en hacerlo, le recomendaría escribir su propio preprocesador para que se ejecute antes de la implementación. Eso debería mantenerse evalalejado del código de producción.
Ruud Helderman

0

Eval no es malvada, solo mal utilizada.

Si creó el código al ingresar o puede confiar en él, está bien. La gente sigue hablando de cómo la entrada del usuario no importa con eval. Bueno más o menos ~

Si hay una entrada del usuario que va al servidor, luego vuelve al cliente, y ese código se está utilizando en eval sin ser desinfectado. Enhorabuena, has abierto la caja de pandora para que los datos de usuario se envíen a quien sea.

Dependiendo de dónde esté la evaluación, muchos sitios web usan SPA, y evaluar podría facilitar al usuario el acceso a las aplicaciones internas que de otra manera no hubieran sido fáciles. Ahora pueden hacer una extensión de navegador falsa que puede grabar en esa evaluación y robar datos nuevamente.

Solo tengo que averiguar cuál es el punto de usar la evaluación. Generar código no es realmente ideal cuando simplemente puedes hacer métodos para hacer ese tipo de cosas, usar objetos o cosas similares.

Ahora un buen ejemplo de uso de eval. Su servidor está leyendo el archivo swagger que ha creado. Muchos de los parámetros de URL se crean en el formato {myParam}. Por lo tanto, le gustaría leer las URL y luego convertirlas en cadenas de plantillas sin tener que hacer reemplazos complejos porque tiene muchos puntos finales. Entonces puedes hacer algo como esto. Tenga en cuenta que este es un ejemplo muy simple.

const params = { id: 5 };

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

// use eval(route); to do something

-1

Codigo de GENERACION. Recientemente escribí una biblioteca llamada Hyperbars que cierra la brecha entre virtual-dom y el manillar . Lo hace analizando una plantilla de manillar y convirtiéndola en hiperescrito . El hiperescrito se genera primero como una cadena y, antes de devolverlo, eval()lo convierte en código ejecutable. he encontradoeval() esta situación particular exactamente lo contrario del mal.

Básicamente de

<div>
    {{#each names}}
        <span>{{this}}</span>
    {{/each}}
</div>

A esto

(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({}))

El rendimiento de eval() no es un problema en una situación como esta también porque solo necesita interpretar la cadena generada una vez y luego reutilizar la salida ejecutable muchas veces.

Puede ver cómo se logró la generación del código si tiene curiosidad aquí .


"El hiperescrito se genera primero como una cadena (...)" Tiene más sentido hacer toda la generación de código en la fase de compilación, escribir el código de hiperescrito resultante en un archivo ejecutable (.js) separado, luego implementar ese archivo para probar y producción. Me encanta la forma en que usas la generación de código. Es solo evaluna pista de que parte de la responsabilidad que pertenece en tiempo de compilación ha pasado al tiempo de ejecución.
Ruud Helderman

-1

Creo que eval es una función muy poderosa para aplicaciones web del lado del cliente y segura ... Tan segura como JavaScript, que no lo son. :-) Los problemas de seguridad son esencialmente un problema del lado del servidor porque, ahora, con herramientas como Firebug, puedes atacar cualquier aplicación de JavaScript.


1
El uso de evaldebe protegerse contra los ataques XSS, lo que no siempre es fácil de hacer.
Benjamin

-1

Eval es útil para la generación de código cuando no tienes macros.

Para un ejemplo (estúpido), si está escribiendo un compilador Brainfuck , probablemente querrá construir una función que realice la secuencia de instrucciones como una cadena, y evaluarla para devolver una función.


O escribe un compilador (que guarda en lugar de ejecutar el código que se genera) o escribe un intérprete (donde cada instrucción tiene una implementación precompilada). Tampoco es un caso de uso para eval.
Ruud Helderman

Si generó código JavaScript y desea ejecutarlo de inmediato (digamos para obtener beneficios de rendimiento sobre la interpretación directa), ese sería un caso de uso para eval.
Erik Haliewicz

Buen punto; Vi un ejemplo en este artículo sobre Blockly . Me sorprende que Google lo recomiende eval, cuando la alternativa ( Función ) es más rápida ( como se explica en MDN ) y más confiable (evita errores impredecibles mediante un mejor aislamiento entre el código generado y otro código 'de apoyo' en la misma página web).
Ruud Helderman

-5

Cuando analiza una estructura JSON con una función de análisis (por ejemplo, jQuery.parseJSON), espera una estructura perfecta del archivo JSON (cada nombre de propiedad está entre comillas dobles). Sin embargo, JavaScript es más flexible. Por lo tanto, puede usar eval () para evitarlo.


44
No lo use ciegamente eval, especialmente. al obtener datos JSON de una fuente de terceros. Ver JSON.Stringify sin comillas en las propiedades? para el enfoque correcto para analizar "JSON sin nombres clave entre comillas".
Rob W

2
Si no utiliza comillas dobles alrededor de los nombres de propiedad, puede ser una representación de cadena de un objeto literal, pero no es JSON . JSON define los nombres de propiedad como a stringy define a stringcomo una secuencia de cero o más caracteres Unicode, entre comillas dobles, utilizando escapes de barra invertida.
Código inútil

Véase el artículo de Nikolas Zakas - "eval () no es malo, apenas entendido mal" nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood
vitmalina

@vitmalina Del artículo de Zakas: "Esto puede ser peligroso si estás tomando la entrada del usuario y ejecutándola a través de eval (). Sin embargo, si tu entrada no es del usuario, ¿hay algún peligro real?" Ese es exactamente el problema. Una vez que su código crece más allá de las proporciones de 'hola mundo', rápidamente se hace imposible demostrar que no está filtrando la entrada del usuario eval. En cualquier aplicación web seria de múltiples inquilinos, con docenas de desarrolladores trabajando en la misma base de código, esto es inaceptable.
Ruud Helderman
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.