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?
javascript
coding-style
eval
Richard Turner
fuente
fuente
Respuestas:
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.
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.
fuente
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 diferenciaeval()
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.fuente
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 ().
fuente
eval
tampoco analizará correctamente todas las cadenas JSON válidas. Por ejemplo,JSON.parse(' "\u2028" ') === "\u2028"
peroeval(' "\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.Consigamos gente real:
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?
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.
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).
fuente
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 lesetTimeout()
permite pasar una función en lugar de evaluar.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.
fuente
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;))
fuente
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.
Debería organizarse como
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,
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é
Opuesto a
Como mi nombre para mostrar, que puede provenir de una base de datos y que no está codificado.
fuente
function (first, last) { return last + ' ' + first }
.eval
es 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 lavalue
opciónalert('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.eval
es ejecutar código que no sea parte del script que escribió. Si no necesita el poder para hacerlo (y casi nunca lo hace), evitareval
ayuda a evitar toda una categoría de problemas. Eso es bueno si su código del lado del servidor no es perfecto.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:
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 nativoeval()
con un nombre de alias (digamosvar noval = eval;
y luego en una función internanoval(expression);
), entonces la evaluación deexpression
puede fallar cuando se refiere a variables que deberían ser parte del cierre, pero en realidad no lo es.fuente
Microsoft explica por qué eval () es lento en su navegador en el Blog de IE , Recomendaciones de rendimiento de IE + JavaScript Parte 2: Ineficiencias de código de JavaScript .
fuente
Línea de fondo
Si creó o desinfectó el código
eval
, nunca es malo .Ligeramente más detallado
eval
es 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 .eval
No 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
eval
sí mismo .fuente
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.
fuente
eval
Raramente 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 queobj.prop
) , cierres, técnicas orientadas a objetos, técnicas funcionales: utilícelas en su lugar.fuente
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.
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.
fuente
<head></head>
obligatorio, incluso si está vacío?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
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.
fuente
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
fuente
Está bien usarlo si tiene control completo sobre el código que se pasa a la
eval
función.fuente
eval
, entonces la gran pregunta es, ¿cuándo tiene sentido que sea una cadena en lugar de un JS real?<script async="true" src="...">
. Ver también: w3bits.com/async-javascriptSolo durante las pruebas, si es posible. También tenga en cuenta que eval () es mucho más lento que otros evaluadores JSON especializados, etc.
fuente
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.
fuente
eval
de una cadena compuesta del contenido de un usuario puede permitir que ese usuario ejecute código en el navegador del otro usuario.eval
sea. Pasa todo el tiempo.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.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.
fuente
¿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.ImageMapType
objeto general para mí, pero necesito decirle la receta, cómo debe construir la URL del mosaico a partir de los parámetroszoom
ycoord
:fuente
tileURL: function (zoom, coord) { return 'http://tile.openstreetmap.org/' + b + '/' + a.x + '/' + a.y + '.png'; },
Mi ejemplo de uso
eval
: import .Cómo se hace generalmente.
Pero con la ayuda
eval
y una pequeña función auxiliar, se ve mucho mejor:importable
podría verse así (esta versión no admite la importación de miembros concretos).fuente
.replace(/name/g, name).replace('path', path)
. Siname
contiene la cadena,"path"
entonces podría obtener sorpresas.components
es 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 mantenerseeval
alejado del código de producción.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.fuente
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
A esto
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í .
fuente
eval
una pista de que parte de la responsabilidad que pertenece en tiempo de compilación ha pasado al tiempo de ejecución.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.
fuente
eval
debe protegerse contra los ataques XSS, lo que no siempre es fácil de hacer.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.
fuente
eval
.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).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.
fuente
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".string
y define astring
como una secuencia de cero o más caracteres Unicode, entre comillas dobles, utilizando escapes de barra invertida.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.