descargar - ¿Cuándo es la evaluación de JavaScript() no malvada?
javascript void 0 como solucionar (23)
Al depurar en Chrome (v28.0.1500.72), encontré 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 se pueden 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 debe necesariamente referirse a la función eval()
nativa. Todo depende del nombre de la función . Entonces, cuando se llama a la eval()
nativa eval()
con un nombre de alias (por ejemplo, var noval = eval;
y luego en una función interna noval(expression);
) la evaluación de la expression
puede fallar cuando se refiere a variables que deberían formar parte del cierre , pero en realidad no lo es.
Estoy escribiendo algo de código JavaScript para analizar las funciones ingresadas por el usuario (para la funcionalidad similar a una hoja de cálculo). Habiendo analizado la fórmula, pude convertirla en JavaScript y ejecutar eval()
para obtener el resultado.
Sin embargo, siempre evito usar eval()
si puedo evitarlo porque es malo (y, con razón o sin razón, siempre he pensado que es aún más malo en JavaScript, porque el código que se va a evaluar podría cambiar por el usuario).
Entonces, ¿cuándo está bien usarlo?
Codigo de GENERACION. Hace poco escribí una biblioteca llamada Hyperbars que cierra la brecha entre virtual-dom y el handlebars . Para ello, analiza una plantilla de manillares y la convierte en hyperscript . El hyperscript se genera primero como una cadena y antes de devolverlo, eval()
para convertirlo en código ejecutable. He encontrado que eval()
en esta situación particular es exactamente lo opuesto al mal.
Básicamente desde
<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 el resultado ejecutable varias veces.
Puedes ver cómo se logró la generación de código si tienes curiosidad here .
Cuando confías en la fuente.
En el caso de JSON, es más o menos difícil manipular la fuente, ya que proviene de un servidor web que usted controla. Mientras el JSON en sí no contenga datos que un usuario haya cargado, no hay inconveniente importante para usar eval.
En todos los demás casos, haría todo lo posible para garantizar que los datos proporcionados por el usuario cumplen con mis reglas antes de enviarlos a eval ().
En el lado del servidor, la evaluación 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 la creación de plantillas de textos
Se puede usar como comparador, digamos que hacemos reglas sobre cómo validar misiones o eventos en CMS
Con esto:
Pueden ser errores en el código y romper las cosas en el servicio, si no se han probado completamente.
Si un pirata informático puede escribir un script en su sistema, entonces usted 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.
En lo que respecta al script del cliente, creo que el tema de la seguridad es un punto discutible. Todo lo que se carga en el navegador está sujeto a manipulación y debe tratarse como tal. Existe un riesgo cero en el uso de 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, le digo que se aleje. La seguridad para prevenir 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 donde las cosas se pueden hacer de otra manera. Sin embargo, hay casos específicos en los que se DEBE utilizar 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>
Está bien usarlo si tiene un control completo sobre el código que se pasa a la función eval
.
Eval es complementario a la compilación que se usa en la creación de plantillas del código. Por plantilla quiero decir que escribe 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 se puede aumentar utilizando el siguiente método; En lugar de ejecutar el script, debe devolver una función.
var a = eval("3 + 5");
Se debe organizar como
var f = eval("(function(a,b) { return a + b; })");
var a = f(3,5);
Caché f sin duda mejorará la velocidad.
También Chrome permite la depuración de tales funciones muy fácilmente.
En cuanto a la seguridad, usar eval o no apenas marcará la diferencia,
- En primer lugar, el navegador invoca todo el script en un sandbox.
- Cualquier código que sea malo en EVAL, es malo en el navegador mismo. El atacante o cualquiera puede inyectar fácilmente un nodo de script en DOM y hacer cualquier cosa si puede evaluar algo. No usar EVAL no hará ninguna diferencia.
- En su mayoría, la seguridad del servidor es deficiente, lo que es perjudicial. La validación de cookies deficiente o la implementación de ACL deficiente en el servidor causan la mayoría de los ataques.
- Una vulnerabilidad reciente de Java, etc., estaba allí en el código nativo de Java. JavaScript fue y está diseñado para ejecutarse en una caja de arena, mientras que los applets fueron diseñados para ejecutarse fuera de una caja de arena con certificados, etc. que llevan a vulnerabilidades y muchas otras cosas.
- 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 favorito. Todas las herramientas de prueba simulan los navegadores de todos modos; Si un atacante quiere hacerle daño, EVAL es su último recurso. Tienen muchas otras formas de lidiar con la seguridad del lado del servidor.
- El navegador DOM no tiene acceso a los archivos y no un nombre de usuario. De hecho, nada en la máquina a la que eval puede dar acceso.
Si la seguridad de su servidor es lo suficientemente sólida para que cualquier persona pueda atacar desde cualquier lugar, no debe preocuparse por EVAL. Como mencioné, si EVAL no existiera, los atacantes tienen muchas herramientas para piratear su servidor, independientemente de la capacidad EVAL de su navegador.
Eval solo es bueno para generar algunas plantillas para realizar un procesamiento complejo de cadenas 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.
La única instancia en la que debería estar utilizando 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ías evitar hacerlo refactorizando.
Me gustaría tomarme un momento para abordar la premisa de su pregunta: que eval () es " malvado ". La palabra " mal ", 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 de aspecto simple". Entonces, ¿cuándo está bien usar algo peligroso? Cuando sepa cuál es el peligro y cuándo 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.
- Performance - eval () ejecuta el intérprete / compilador. Si su código es compilado, entonces esto es un gran éxito, ya que necesita llamar a un compilador posiblemente pesado en medio del tiempo de ejecución. Sin embargo, JavaScript es en su mayoría un lenguaje interpretado, lo que significa que llamar a eval () no es un gran éxito 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 bajo privilegios elevados. Por ejemplo, un programa que se ejecuta como administrador / raíz nunca querría evaluar () la entrada del usuario, porque esa entrada podría ser potencialmente "rm -rf / etc / important-file" o peor. Una vez más, JavaScript en un navegador no tiene ese problema, porque el programa se está ejecutando en la cuenta del usuario de todos modos. El servidor del lado de JavaScript podría tener ese problema.
En su caso específico. Por lo que entiendo, usted mismo está generando las cadenas, 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 Difícil asegurar esto en el caso general). Además, si está ejecutando en el navegador, entonces la inyección de código es un riesgo bastante menor, creo.
En cuanto al rendimiento, tendrás que ponderar eso contra la facilidad de codificación. En mi opinión, si está analizando la fórmula, también puede 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 no se notará. Parece que eval () en este caso no es más malo que cualquier otra función que pueda ahorrarle tiempo.
Mi ejemplo de usar eval
: import .
Como se suele hacer.
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 de eval
y una pequeña función de ayuda, se ve mucho mejor:
var components = require(''components'');
eval(importable(''components'', ''Button'', ''ComboBox'', ''CheckBox'', ...));
importable
puede parecer (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;
}
Microsoft explica por qué eval () es lento en su navegador en el Blog de IE , IE + Recomendaciones de rendimiento de JavaScript Parte 2: Ineficiencias de código de JavaScript .
No hay 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, ya que 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 debes evaluar () para analizar código escrito por otros usuarios como en un foro.
Sólo durante la prueba, si es posible. También tenga en cuenta que eval () es mucho más lento que otros evaluadores especializados de JSON, etc.
Tiendo a seguir el consejo de Crockford para eval()
, y lo evito por completo. Incluso las formas que parecen requerirlo no lo hacen. Por ejemplo, el setTimeout()
permite pasar una función en lugar de eval.
setTimeout(function() {
alert(''hi'');
}, 1000);
Incluso si es 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 extraño, en el peor, exponer algo malo.
Vamos a obtener gente real:
Cada navegador principal ahora tiene una consola incorporada que su posible pirata informático puede usar abundantemente para invocar cualquier función con cualquier valor. ¿Por qué se molestan en usar una declaración de evaluación, incluso si pudieran?
Si tarda 0.2 segundos en compilar 2000 líneas de JavaScript, ¿cuál es la degradación de mi rendimiento si evalúo cuatro líneas de JSON?
Incluso la explicación de Crockford para ''eval es malvada'' es débil.
"eval is Evil
The eval function is the most misused feature of JavaScript. Avoid it"
Como el mismo Crockford podría decir: "Este tipo de afirmación tiende a generar neurosis irracional. No lo compre".
Comprender eval y saber cuándo puede ser útil es mucho más importante. Por ejemplo, eval es una herramienta sensata para evaluar las respuestas del servidor generadas por su software.
BTW: 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).
Vi personas que abogan por no usar eval, porque es malvado , pero vi a las mismas personas que usan Function y setTimeout dinámicamente, por lo que usan eval bajo los capítulos : D
Por cierto, si su caja de arena no es lo suficientemente segura (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 JavaScript puede ser malo, porque en JavaScript puede sobrescribir cualquier función y no puede estar seguro de que está utilizando 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 este post es:
Si REALMENTE lo necesita (el 80% del tiempo NO es necesario evaluar ) y está seguro de lo que está haciendo, solo use eval (o mejor Función;)), los cierres y la POO cubren el 80/90% del En el caso de que eval pueda reemplazarse utilizando otro tipo de lógica, el resto es un 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;))
eval()
no es malo. O, si lo es, es malvado de la misma manera que la reflexión, E / S de archivos / redes, subprocesos e IPC son "malvados" 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 debería usarlo. Si no es así, entonces no deberías. Simple como eso.
eval
rara vez es la elección correcta. Si bien puede haber numerosos casos en los que puede lograr lo que necesita lograr al concatenar un script y ejecutarlo sobre la marcha, normalmente tiene a su disposición técnicas mucho más potentes y fáciles de mantener: notación de matriz asociativa ( obj["prop"]
es lo mismo que obj.prop
), cierres, técnicas orientadas a objetos, técnicas funcionales; en su lugar, utilícelas.
¿Cuándo es la evaluación de JavaScript () no malvada?
Siempre estoy tratando de desalentar el uso de eval . Casi siempre, una solución más limpia y mantenible está disponible. Eval json.org . Eval agrega al infierno de mantenimiento . No sin razón, está mal visto por maestros como Douglas Crockford.
Pero encontré un ejemplo donde debería usarse:
Cuando necesites pasar la expresión.
Por ejemplo, tengo una función que construye un objeto general google.maps.ImageMapType
para mí, pero necesito decirle la receta, cómo debería construir la URL del mosaico a partir de los parámetros zoom
y 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);
},
....
});
}
Eval es útil para la generación de código cuando no tiene macros.
Por ejemplo, si está escribiendo un compilador de Brainfuck , probablemente querrá construir una función que realice la secuencia de instrucciones como una cadena y evaluarla para devolver una función.
Cuando analiza una estructura JSON con una función de análisis (por ejemplo, jQuery.parseJSON), se 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, puedes usar eval () para evitarlo.
Mi creencia es que eval es una función muy poderosa para aplicaciones web del lado del cliente y segura ... Tan seguro como JavaScript, que no lo son. :-) Los problemas de seguridad son esencialmente un problema del lado del servidor porque, ahora, con una herramienta como Firebug, puede atacar cualquier aplicación de JavaScript.
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 el setTimeout).
Para mí, el mal no es un desempeño o incluso un problema de seguridad (bueno, indirectamente son ambos). Todos esos usos innecesarios de eval se añaden a un infierno de mantenimiento. Las herramientas de refactorización son desechadas. La búsqueda de código es difícil. Los efectos imprevistos de esos males son legión.