Una plataforma de aprendizaje que permite a los estudiantes componer consultas de MongoDB ha dejado de utilizar el módulo vm de Node.js y ahora analiza cada consulta en un árbol de sintaxis abstracta (AST) antes de su ejecución. El cambio elimina un sandbox que podría ser vulnerado, protegiendo el backend de código arbitrario que una cadena malformada podría inyectar.
Por qué falló el sandbox original
La primera implementación envolvía un manejador de base de datos activo en una llamada a vm.runInContext e intentaba restringir a los usuarios mediante una expresión regular. Solo permitía nombres de métodos como find o aggregate; se suponía que cualquier otro identificador sería filtrado.
Dos fallos hicieron que ese enfoque fuera inseguro:
- El filtrado por regex es evadible. JavaScript permite que el código utilice la notación de corchetes (
obj["constructor"]) para acceder a cualquier propiedad. Un atacante puede recuperar el constructorFunction, construir una nueva función y ejecutar cualquier código que desee. La regex nunca detecta el acceso a la propiedad subyacente porque el código fuente puede reescribirse de innumerables formas. vmno es un límite de seguridad. La documentación de Node establece quevmaísla el objeto global, pero no el proceso completo. Al inyectar una conexión de base de datos activa en el sandbox, el código dentro del contexto conservaba la capacidad de llamar a cualquier método de esa conexión, incluidos aquellos que escriben o eliminan datos. El sandbox no impedía que el código afectara al proceso host.
La solución basada en AST
El equipo sustituyó la ejecución de código por el análisis estático. Las cadenas de consulta ahora se pasan al analizador acorn, que produce un AST: una representación en árbol de la estructura sintáctica del código. El AST se examina nodo por nodo mediante una lista blanca estricta:
- Los literales, arrays y objetos solo se permiten cuando aparecen como valores simples.
- Las llamadas a métodos se limitan a un conjunto predefinido (
find,sort,limit, etc.). Cualquier otra llamada es rechazada. - El acceso a propiedades computadas (p. ej.,
obj[expr]) o cualquier tipo de nodo que no esté explícitamente listado provoca un fallo inmediato.
Debido a que el analizador trabaja sobre el árbol y no sobre el texto sin procesar, no puede ser engañado por ortografías alternativas o trucos de notación de corchetes. Una cadena de constructores que habría pasado desapercibida para la regex aparece como un nodo no reconocido y es rechazada antes de que se ejecute cualquier código.
Qué significa esto para la seguridad
El nuevo diseño sigue una filosofía de "denegar por defecto":
- Define qué está permitido, no qué está prohibido. Las listas blancas textuales no pueden ser exhaustivas; un AST tiene un conjunto finito de tipos de nodos, lo que hace que la comprobación exhaustiva sea viable.
- Nunca expongas recursos activos dentro de un sandbox. Pasar un manejador de base de datos a un contexto aislado le da al código en el sandbox una línea directa al backend. El enfoque del analizador nunca entrega un objeto activo al código del usuario; solo extrae la intención de la consulta.
- Valida la estructura y luego ejecuta de forma segura. Una vez que el AST supera la validación, la plataforma traduce las llamadas permitidas en métodos reales del controlador de MongoDB utilizando su propia ruta de código de confianza.
Qué vigilar a continuación
- Audita cualquier uso de
eval,new Functionovmen tu base de código. Incluso una lista blanca puede ser subvertida por la naturaleza dinámica de JavaScript. - Adopta el análisis AST para código generado por el usuario siempre que sea posible. Librerías como
acorn,esprimaobabel-parserhacen que la transformación sea sencilla. - Limita la exposición de objetos activos. Si una conexión de base de datos, un manejador de archivos o un socket de red debe ser accesible, envuélvelo en un proxy que solo exponga los métodos mínimos que pretendes permitir.
- Automatiza las pruebas de casos límite. Genera consultas que utilicen la notación de corchetes, claves computadas o manipulación de prototipos para verificar que tu analizador las rechaza.
Conclusión
Confiar en vm.runInContext como un sandbox proporciona una falsa sensación de seguridad; los filtros regex no pueden cubrir la flexible sintaxis de JavaScript, y el sandbox no aísla los recursos del proceso. Analizar la entrada del usuario en un AST y aplicar una lista blanca solo a los nodos que comprendes proporciona una barrera concreta y mantenible que detiene el código malicioso antes de que se ejecute. Si tu plataforma permite a los usuarios escribir código, sustituye la ejecución de estilo eval por el análisis estático hoy mismo.
