Usar ES2015 Proxy para divertirse y obtener beneficios
Alon Niv
23 de agosto de 2016
0 minutos de lecturaMucho se ha escrito sobre ES2015, con sus funciones flecha, declaraciones de variables con ámbito y sus polémicas clases. Sin embargo, una función no ha recibido mucha atención hasta ahora: Proxy.Como desarrolladores de JS, no estamos acostumbrados a depender de mecanismos de interceptación en todo nuestro código, pero tienen varias aplicaciones muy útiles. Por nombrar algunas:
Pruebas, simulaciones y monkey patching
Los patrones de diseño
ObserveryVisitorAbstracciones para conceptos complejos
Hasta ahora, el lenguaje no nos había proporcionado ningún mecanismo de este tipo.Considero que Proxy resuelve este problema, sin perder la esencia del JS que conocemos y nos encanta (es decir, sin métodos [Symbol.__setattr__] para nuestros objetos).En esta publicación, daré un ejemplo de las trampas más comunes (get, set, has y deleteProperty) y, siguiendo el espíritu de Snyk, hablaremos de dependencias.
Nuestro objetivo
Queremos crear un paquete que permita a los desarrolladores usar require de forma segura, limitando el acceso a módulos específicos. Al fin y al cabo, no querríamos confiarle nuestro fs a un módulo de utilidades funcionales, ¿verdad?
Preparar el terreno
El constructor de Proxy acepta 2 parámetros:
target: es el objeto que queremos convertir en proxyhandler: es un objeto que contiene la especificación de las trampas que queremos manejar (en este sentido, una trampa es una función que se llama cuando se producen ciertos eventos relacionados con el proxy, como acceder, establecer o eliminar una propiedad); a continuación, veremos algunos ejemplos.
Cómo funciona require
Solo incluyo esta parte porque, aunque la documentación de node lo explica muy bien, estamos por hacer cosas ingeniosas con la caché de módulos y quiero asegurarme de que el flujo de require quede claro:
El módulo
xrequireel móduloyEl nombre
yse resuelve como una ruta absoluta (o no, si es un módulo integrado, comofs)El nombre resuelto se obtiene de la caché, sin comprobar si existe
Si el valor obtenido está definido (es un objeto Module), se devuelven los valores exportados
De lo contrario, node obtiene el archivo del sistema de archivos, lo compila en un objeto Module, lo guarda en la caché y devuelve sus valores exportados.
Esta caché es un singleton global, accesible mediante require('module')._cache y require.cache, y es un objeto JS simple.
Por fin, algo de código
Para simplificar, supongamos que solo queremos ofrecer una interfaz de lista de bloqueo para los módulos que no debería usar el módulo requerido. Se vería así:
Bien, ya tenemos una idea de cómo se verá. Empecemos a escribir el código:
Genial, ya terminamos, ¿verdad?Bueno, no exactamente. Sí, nos aseguramos de que nuestro módulo malicioso no pueda usar require para acceder a un módulo bloqueado, pero puede recurrir a otros trucos sucios:
Nota al margen sobre require.cache: si quieres corromper un módulo específico en la caché de módulos, usa require.cache.Sin embargo, si quieres reemplazar todo el mecanismo de almacenamiento en caché, usa require('module')._cache = ....Modifiquemos el código para manejar esta situación:
Y… ahora sí terminamos… ¿verdad? No. Veamos este código malicioso:
Agreguemos unos últimos detalles:
Y ahora sí terminamos. O al menos eso creo.
¿Se te ocurre cómo evadir este proxy? ¡Compártelo con nosotros en Twitter @snyksec!
El repositorio de esta prueba de concepto está disponible aquí.
Comienza con Capture the Flag
Aprende a resolver desafíos de Capture the Flag con nuestro taller virtual 101 a pedido.


