La función eval es una manera poderosa y fácil de generar código dinámicamente, ¿cuáles son las advertencias?
La función eval es una manera poderosa y fácil de generar código dinámicamente, ¿cuáles son las advertencias?
Respuestas:
El uso incorrecto de eval abre su código para ataques de inyección
La depuración puede ser más desafiante (sin números de línea, etc.)
el código evaluado se ejecuta más lentamente (no hay oportunidad de compilar / almacenar en caché el código evaluado)
Editar: como señala @Jeff Walden en los comentarios, el n. ° 3 es menos cierto hoy que en 2008. Sin embargo, aunque puede ocurrir un almacenamiento en caché de los scripts compilados, esto solo se limitará a los scripts que se evalúen repetidamente sin ninguna modificación. Un escenario más probable es que está evaluando scripts que han sufrido ligeras modificaciones cada vez y, como tal, no se pueden almacenar en caché. Digamos que ALGUNOS códigos evaluados se ejecutan más lentamente.
badHackerGuy'); doMaliciousThings();
y si toma mi nombre de usuario, lo concatena en un script y lo evalúa en los navegadores de otras personas, entonces puedo ejecutar cualquier javascript que quiera en sus máquinas (por ejemplo, forzarlos a hacer +1 en mis publicaciones, publicar sus datos en mi servidor, etc.)
debugger;
declaración a su código fuente. Esto detendrá la ejecución de su programa en esa línea. Luego, después de eso, puede agregar puntos de interrupción de depuración como si fuera solo otro archivo JS.
eval no siempre es malo. Hay momentos en que es perfectamente apropiado.
Sin embargo, eval es sobreutilizado actualmente e históricamente por personas que no saben lo que están haciendo. Eso incluye a personas que escriben tutoriales de JavaScript, desafortunadamente, y en algunos casos esto puede tener consecuencias de seguridad o, más a menudo, errores simples. Entonces, cuanto más podamos hacer para lanzar un signo de interrogación sobre eval, mejor. Cada vez que usa eval necesita verificar con cordura lo que está haciendo, porque es probable que lo esté haciendo de una manera mejor, más segura y más limpia.
Para dar un ejemplo demasiado típico, para establecer el color de un elemento con una identificación almacenada en la variable 'papa':
eval('document.' + potato + '.style.color = "red"');
Si los autores del tipo de código anterior tuvieran una idea sobre los conceptos básicos de cómo funcionan los objetos JavaScript, se habrían dado cuenta de que se pueden usar corchetes en lugar de nombres de puntos literales, lo que evita la necesidad de evaluar:
document[potato].style.color = 'red';
... que es mucho más fácil de leer y con menos errores.
(Pero entonces, alguien que / realmente / sabía lo que estaba haciendo diría:
document.getElementById(potato).style.color = 'red';
que es más confiable que el viejo truco poco fiable de acceder a elementos DOM directamente desde el objeto del documento).
JSON.parse()
lugar de eval()
JSON?
Creo que es porque puede ejecutar cualquier función de JavaScript desde una cadena. Usarlo hace que sea más fácil para las personas inyectar código falso en la aplicación.
Me vienen a la mente dos puntos:
Seguridad (pero siempre que genere la cadena para evaluarla usted mismo, esto podría no ser un problema)
Rendimiento: hasta que se desconozca el código a ejecutar, no se puede optimizar. (sobre JavaScript y rendimiento, ciertamente la presentación de Steve Yegge )
eval
evaluado y luego, ese pequeño fragmento de código se ejecuta en el navegador B del usuario
Pasar la entrada del usuario a eval () es un riesgo de seguridad, pero también cada invocación de eval () crea una nueva instancia del intérprete de JavaScript. Esto puede ser un gran recurso.
Principalmente, es mucho más difícil de mantener y depurar. Es como una goto
. Puede usarlo, pero hace que sea más difícil encontrar problemas y más difícil para las personas que pueden necesitar hacer cambios más adelante.
Una cosa a tener en cuenta es que a menudo puede usar eval () para ejecutar código en un entorno de otro modo restringido: los sitios de redes sociales que bloquean funciones específicas de JavaScript a veces se pueden engañar al dividirlos en un bloque eval.
eval('al' + 'er' + 't(\'' + 'hi there!' + '\')');
Entonces, si está buscando ejecutar algún código JavaScript donde de otra manera no se permitiría ( Myspace , lo estoy mirando ...), entonces eval () puede ser un truco útil.
Sin embargo, por todas las razones mencionadas anteriormente, no debe usarlo para su propio código, donde tiene el control completo: simplemente no es necesario, y es mejor relegarlo al estante de 'trucos de JavaScript difíciles'.
[]["con"+"struc"+"tor"]["con"+"struc"+"tor"]('al' + 'er' + 't(\'' + 'hi there!' + '\')')()
A menos que permita que eval () sea un contenido dinámico (a través de cgi o input), es tan seguro y sólido como el resto de JavaScript en su página.
Junto con el resto de las respuestas, no creo que las declaraciones de evaluación puedan tener una minimización avanzada.
Es un posible riesgo de seguridad, tiene un alcance de ejecución diferente y es bastante ineficiente, ya que crea un entorno de secuencias de comandos completamente nuevo para la ejecución del código. Vea aquí para más información: eval .
Sin embargo, es bastante útil y se usa con moderación para agregar una gran cantidad de buenas funciones.
A menos que esté 100% seguro de que el código que se está evaluando proviene de una fuente confiable (generalmente su propia aplicación), entonces es una forma segura de exponer su sistema a un ataque de secuencias de comandos entre sitios.
Sé que esta discusión es antigua, pero realmente me gusta esto enfoque de Google y quería compartir ese sentimiento con otros;)
La otra cosa es que cuanto mejor obtienes, más tratas de entender y, finalmente, simplemente no crees que algo es bueno o malo solo porque alguien lo dijo :) Este es un video muy inspirador que me ayudó a pensar más por mí mismo :) Las BUENAS PRÁCTICAS son buenas, pero no las uses sin pensar :)
No es necesariamente tan malo siempre que sepa en qué contexto lo está utilizando.
Si su aplicación está usando eval()
para crear un objeto de algún JSON que ha regresado de un XMLHttpRequest a su propio sitio, creado por su código confiable del lado del servidor, probablemente no sea un problema.
El código JavaScript del lado del cliente no confiable no puede hacer tanto de todos modos. Siempre que lo que está ejecutando eval()
proviene de una fuente razonable, está bien.
Reduce en gran medida su nivel de confianza sobre la seguridad.
Si desea que el usuario ingrese algunas funciones lógicas y evalúe AND Y, entonces la función de evaluación de JavaScript es perfecta. Puedo aceptar dos cadenas y eval(uate) string1 === string2
, etc.
Si ve el uso de eval () en su código, recuerde el mantra "eval () is evil".
Esta función toma una cadena arbitraria y la ejecuta como código JavaScript. Cuando el código en cuestión se conoce de antemano (no se determina en tiempo de ejecución), no hay razón para usar eval (). Si el código se genera dinámicamente en tiempo de ejecución, a menudo hay una mejor manera de lograr el objetivo sin eval (). Por ejemplo, simplemente usar la notación de corchetes para acceder a las propiedades dinámicas es mejor y más simple:
// antipattern
var property = "name";
alert(eval("obj." + property));
// preferred
var property = "name";
alert(obj[property]);
El uso eval()
también tiene implicaciones de seguridad, ya que podría estar ejecutando código (por ejemplo, procedente de la red) que ha sido alterado. Este es un antipatrón común cuando se trata con una respuesta JSON de una solicitud de Ajax. En esos casos, es mejor usar los métodos integrados de los navegadores para analizar la respuesta JSON para asegurarse de que sea segura y válida. Para navegadores que no admitenJSON.parse()
forma nativa, puede usar una biblioteca de JSON.org.
También es importante recordar que pasar cadenas a setInterval()
, setTimeout()
y el Function()
constructor es, en su mayor parte, similar al uso eval()
y, por lo tanto, debe evitarse.
Detrás de escena, JavaScript todavía tiene que evaluar y ejecutar la cadena que pasa como código de programación:
// antipatterns
setTimeout("myFunc()", 1000);
setTimeout("myFunc(1, 2, 3)", 1000);
// preferred
setTimeout(myFunc, 1000);
setTimeout(function () {
myFunc(1, 2, 3);
}, 1000);
El uso del nuevo constructor Function () es similar a eval () y debe abordarse con cuidado. Podría ser una construcción poderosa, pero a menudo se usa mal. Si absolutamente debes usareval()
, puede considerar usar una nueva función () en su lugar.
Hay un pequeño beneficio potencial porque el código evaluado en la nueva función () se ejecutará en un ámbito de función local, por lo que las variables definidas con var en el código que se evalúa no se convertirán en globales automáticamente.
Otra forma de prevenir los globales automáticos es envolver la
eval()
llamada en una función inmediata.
Además de los posibles problemas de seguridad si está ejecutando código enviado por el usuario, la mayoría de las veces hay una mejor manera que no implica volver a analizar el código cada vez que se ejecuta. Las funciones anónimas o las propiedades de objeto pueden reemplazar la mayoría de los usos de eval y son mucho más seguras y rápidas.
Esto puede convertirse en un problema ya que la próxima generación de navegadores sale con un poco de compilador de JavaScript. El código ejecutado a través de Eval puede no funcionar tan bien como el resto de su JavaScript en estos navegadores más nuevos. Alguien debería hacer algunos perfiles.
Este es uno de los buenos artículos que hablan sobre eval y cómo no es un mal: http://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood/
No estoy diciendo que debas salir corriendo y comenzar a usar eval () en todas partes. De hecho, hay muy pocos casos de uso buenos para ejecutar eval (). Definitivamente, existen preocupaciones con respecto a la claridad del código, la capacidad de depuración y, ciertamente, el rendimiento que no deben pasarse por alto. Pero no debe tener miedo de usarlo cuando tiene un caso en el que eval () tiene sentido. Intente no usarlo primero, pero no permita que nadie lo asuste y piense que su código es más frágil o menos seguro cuando eval () se usa adecuadamente.
eval () es muy poderoso y puede usarse para ejecutar una declaración JS o evaluar una expresión. Pero la pregunta no se trata de los usos de eval (), sino que digamos de alguna manera cómo la cadena que ejecuta con eval () se ve afectada por una parte maliciosa. Al final estarás ejecutando código malicioso. Con el poder viene una gran responsabilidad. Así que úsalo sabiamente si lo estás usando. Esto no se relaciona mucho con la función eval () pero este artículo tiene bastante buena información: http://blogs.popart.com/2009/07/javascript-injection-attacks/ Si está buscando los conceptos básicos de eval () mira aquí: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
El motor de JavaScript tiene una serie de optimizaciones de rendimiento que realiza durante la fase de compilación. Algunos de estos se reducen a la capacidad de analizar esencialmente el código estáticamente a medida que se relaja, y predeterminar dónde están todas las declaraciones de variables y funciones, por lo que se necesita menos esfuerzo para resolver los identificadores durante la ejecución.
Pero si el motor encuentra una evaluación (..) en el código, esencialmente tiene que suponer que toda su conciencia de la ubicación del identificador puede ser inválida, porque no puede saber en el momento exacto qué código puede pasarle a evaluación (..) para modificar el alcance léxico, o el contenido del objeto con el que puede pasar para crear un nuevo alcance léxico para ser consultado.
En otras palabras, en el sentido pesimista, la mayoría de esas optimizaciones que haría no tienen sentido si eval (..) está presente, por lo que simplemente no realiza las optimizaciones en absoluto.
Esto lo explica todo.
Referencia:
https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20&%20closures/ch2.md#eval
https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20&%20closures/ch2.md#performance
No siempre es una mala idea. Tomemos, por ejemplo, la generación de código. 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 que posteriormente es utilizado por virtual-dom. El hiperescrito se genera primero como una cadena y, antes de devolverlo, eval()
lo convierte en código ejecutable. En eval()
esta situación particular he encontrado 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 porque solo necesita interpretar la cadena generada una vez y luego reutilizar la salida ejecutable muchas veces.
Puedes ver cómo se logró la generación del código si tienes curiosidad aquí .
Me atrevería a decir que realmente no importa si usa eval()
JavaScript que se ejecuta en navegadores. * (Advertencia)
Todos los navegadores modernos tienen una consola de desarrollador donde puedes ejecutar JavaScript arbitrario de todos modos y cualquier desarrollador semi-inteligente puede mirar tu fuente JS y poner lo que necesiten en la consola de desarrollo para hacer lo que deseen.
* Siempre y cuando los puntos finales de su servidor tengan la validación y desinfección correctas de los valores proporcionados por el usuario, no debería importar lo que se analice y evalúe en su javascript del lado del cliente.
eval()
Sin embargo, si tuviera que preguntar si es adecuado para usar en PHP, la respuesta es NO , a menos que incluya en la lista blanca cualquier valor que pueda pasar a su sentencia eval.
No intentaré refutar nada de lo dicho hasta ahora, pero ofreceré este uso de eval () que (hasta donde yo sé) no se puede hacer de otra manera. Probablemente hay otras formas de codificar esto, y probablemente formas de optimizarlo, pero esto se hace a mano y sin ningún tipo de campanas y silbatos por razones de claridad para ilustrar un uso de eval que realmente no tiene otras alternativas. Es decir: nombres de objeto dinámicamente (o más exactamente) creados mediante programación (en oposición a los valores).
//Place this in a common/global JS lib:
var NS = function(namespace){
var namespaceParts = String(namespace).split(".");
var namespaceToTest = "";
for(var i = 0; i < namespaceParts.length; i++){
if(i === 0){
namespaceToTest = namespaceParts[i];
}
else{
namespaceToTest = namespaceToTest + "." + namespaceParts[i];
}
if(eval('typeof ' + namespaceToTest) === "undefined"){
eval(namespaceToTest + ' = {}');
}
}
return eval(namespace);
}
//Then, use this in your class definition libs:
NS('Root.Namespace').Class = function(settings){
//Class constructor code here
}
//some generic method:
Root.Namespace.Class.prototype.Method = function(args){
//Code goes here
//this.MyOtherMethod("foo")); // => "foo"
return true;
}
//Then, in your applications, use this to instantiate an instance of your class:
var anInstanceOfClass = new Root.Namespace.Class(settings);
EDITAR: por cierto, no sugeriría (por todas las razones de seguridad señaladas hasta ahora) que base sus nombres de objetos en la entrada del usuario. Sin embargo, no puedo imaginar ninguna buena razón por la que quieras hacer eso. Aún así, pensé en señalar que no sería una buena idea :)
namespaceToTest[namespaceParts[i]]
, sin necesidad de eval aquí, por lo que la if(typeof namespaceToTest[namespaceParts[i]] === 'undefined') { namespaceToTest[namespaceParts[i]] = {};
única diferencia para elelse namespaceToTest = namespaceToTest[namespaceParts[i]];
Recolección de basura
La recolección de basura de los navegadores no tiene idea de si el código evaluado puede eliminarse de la memoria, por lo que simplemente lo mantiene almacenado hasta que se vuelve a cargar la página. No está mal si sus usuarios solo están en su página en breve, pero puede ser un problema para las aplicaciones web.
Aquí hay un script para demostrar el problema
https://jsfiddle.net/CynderRnAsh/qux1osnw/
document.getElementById("evalLeak").onclick = (e) => {
for(let x = 0; x < 100; x++) {
eval(x.toString());
}
};
Algo tan simple como el código anterior hace que se almacene una pequeña cantidad de memoria hasta que la aplicación muera. Esto es peor cuando el script evadido es una función gigante, y se llama a intervalos.